阅读本文前,如果对Java泛型不够了解,不妨先阅读我之前写的两篇说Java泛型的文章
重拾Java泛型 上篇
重拾Java泛型 下篇
Java
public static super T>> T max(List extends T> list){
Iterator extends T> iterator = list.iterator();
T result = iterator.next();
while (iterator.hasNext()){
T t = iterator.next();
if(t.compareTo(result) > 0){
result = t;
}
}
return result;
}
Kotlin
fun in T>> max(list: List): T {
val iterator = list.iterator()
var result: T = iterator.next()
while (iterator.hasNext()) {
val t = iterator.next()
if (t.compareTo(result) > 0) {
result = t
}
}
return result
}
查看Kotlin里的Comparable类和List类源码,Comparable类定义,List类定义,所以可以不必重复表明Comparable的逆变能力和List的协变能力。
fun > max(list: List): T {
val iterator = list.iterator()
var result: T = iterator.next()
while (iterator.hasNext()) {
val t = iterator.next()
if (t.compareTo(result) > 0) {
result = t
}
}
return result
}
不管是泛型类还是泛型方法,泛型的使用分为两个步骤
上面的泛型方法,以Java泛型方法为例,泛型声明部分是:
extends Comparable super T>>
引入泛型类型名称为T,这个数据类型的关系是实现Comparable的类。
使用泛型的部分如下:
List extends T>
入参的集合具有对类型T的协变能力。
所以,有些书或者网上资料,会出现两个概念:声明点变型,使用点变型。这个概念说的就是这件事
再来看下in位置,和out位置的概念
所以,类型参数T上的关键字out有两方面含义:
我们再看一个有趣的例子
我有一个方法,入参的一个集合,我希望入参集合元素是EditText的子类的集合,如何构建满足这个关系的方法呢?至少有下面的两种方式:
fun test2(list: MutableList){
}
fun test3(list: MutableList){
}
上面test2, test3方法都能实现预期的效果。不同的是,test2使用协变来实现,也意味着只能读不能写操作,test2不是泛型方法。test3方法通过在声明表明泛型参数的类型关系来实现,可读可写,它是泛型方法。
对应的Java代码如下
private void test2(List extends EditText> list){
}
private void test3(List list){
}
Java
private void ensureTrailingPeriod(T seq){
}
kotlin
fun <T> ensureTrailingPeriod(seq: T) where T: Serializable, T: CharSequence{
}
这是Java语法上很大的不同,Kotlin要实现多约束,使用到where关键词,这是全新的表示法。
来看一段Java代码:
private void test(T t){
if(t instanceof String){
String s = (String)t;
}else if(t instanceof Integer){
Integer i = (Integer)t;
}
}
入参是泛型的,通过instanceof来判断泛型具体是哪个类型。再看一段代码
private void test2(Object o){
//编译不通过
if(o instanceof T){
}
}
泛型作为参数类型时,编译不通过。如果要完成上述的逻辑,要怎么实现?可以通过class近似等效实现
class ClassJudge{
Class kind;
public ClassJudge(Class kind) {
this.kind = kind;
}
public boolean isInstance(Object o){
return kind.isInstance(o);
}
}
调用
Integer a = 12;
boolean instance = new ClassJudge(String.class).isInstance(a);
System.out.println("instance = " + instance);
Kotlin在语言层面作出了支持,对上述Java代码test2方法,kotlin代码如下:
inline fun test2(any: Any){
if(any is T){
}
}
因此需要具备两个条件:
在Kotlin里,非空类型是可空类型的子类型。
Java实现的协变
List extends Number> list = new ArrayList();
Kotlin实现的协变
val list: MutableList<out Number> = arrayListOf<Int>()
Java实现的逆变
List super Integer> list = new ArrayList();
对应的Kotlin逆变的实现:
val list: MutableList<in Int> = arrayListOf()
变型(协变和逆变)涉及到集合元素,集合类。协变讲的是两个集合的元素是子类关系,这两个集合也是子类关系,有了子类关系,就可以用多态表示。逆变的关系是反过来的,逆变说得是,两个集合的元素是父类关系,这两个集合却能成为子类关系。
由此可见,在泛型里,extends 不全等于 :。extends代表子类型关系和协变,而Kotlin的 :只代表子类型关系。
T> = extends T>
<in T> = super T>
星投影如何使用
<*> = <out Any?>
<*> = <in nothing>
投影一词,顾名思义,是对Any?的投影,获得了Any?部分的能力—Any?协变的能力,即失去写操作,只能读操作。
fun printFirst(list: List<*>){
if(list.isNotEmpty()){
println(list.first())
}
}
所有不安全的逻辑都被隐藏在类的主体中,通过把这些逻辑局部化到一个分开的位置,预防了误用,保证了它不会被错误地使用。
Java和Kotlin默认,泛型参数都是可空的,以Kotlin为例
class Processor<T> {
fun process(value: T){
value?.hashCode()
}
}
泛型参数是可空类型,可以理解为默认实现
class Processor2<T: Any> {
fun process(value: T){
value.hashCode()
}
}
所以,泛型 和是有区别的,前者是可空类型,后者是非空类型,确切说,和
Kotlin和Java的差异,体现在语法和功能两个方面
语法上
功能上:
1. 新增了实化参数类型
仔细说说Java中的泛型
Java 泛型