Kotlin高级——泛型
开始
泛型类
1
2
3
class MyClass<T> {
fun method(t: T): T
}
泛型函数
1
2
3
fun <T> method(t: T): T {
// statements
}
泛型约束
用于约束泛型参数的上界,类似java的extends,默认的上界是Any?
1
class Person<T : String>
当类型需要多个上界时,使用where语句,T类型必须同时继承/实现A类型和B类型
1
class Person<T> where T : A, T : B
类型擦除
在Java和Kotlin中都有类型擦除,指的是在使用泛型时,泛型参数会在编译期间去除,也就是List<T>
的泛型参数T会被擦除,List<T>
就变成List,类中使用到泛型参数的地方都会被赋予类型
随之带来一个问题,就是无法在运行时判断泛型参数的类型(Kotlin可以通过reified关键字实现),同时无法描述泛型参数的类型关系,其中一个例子是List<Object>
不是List<String>
的父类,这样的现象称为不变
协变与逆变
协变和逆变用于解决不变问题,二者统称为型变
协变和逆变通常用在函数参数和返回值的类型声明上,用于扩展容器输入和输出的类型范围,并不能进行实际地转换,如果要实现类型转换,只能创建新对象,手动进行转换
星投影:类似java的泛型通配符?
,用于接收不确定的泛型信息,只能读取值(向外输出值),相当于<out Any?>
Java实现:使用extends实现协变,使用super实现逆变,只支持使用处型变
1 2 3 4 5 6 7 8 9
interface Collection<T> { // 协变:向下扩展类型范围,只能安全读取,确保读取的都是T的子类 Collection<? extends T> method1(); void method2(Collection<? extends T> c); // 逆变:向上扩展类型范围,只能安全写入,确保写入的都是T的子类 Collection<? super T> method3(); void method4(Collection<? super T> c); }
Kotlin实现:使用out实现协变,使用in实现逆变,支持声明处型变
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
// 使用处型变 interface Collection<T> { // 协变 fun method1(): Collection<out T>; fun method2(c: Collection<out T>); // 逆变 fun method3(): Collection<in T>; fun method4(c: Collection<in T>); } // 声明处型变 interface Collection<out T> { // T只支持协变 fun method1(): Collection<T>; fun method2(c: Collection<T>); } interface Collection<in T> { // T只支持逆变 fun method3(): Collection<in T>; fun method4(c: Collection<in T>); }
协变逆变原理
核心原理为一个父类引用可以无障碍地接收一个子类对象,协变和逆变都是基于这一点实现的
容器泛型使用协变还是逆变,与它作为形参还是返回值是无关的,要看该容器的值的使用场景
- 当容器在消费场景时,容器需要被读取,因此外部需要使用一个父类引用接收读取值,因此容器泛型使用协变声明,确保容器内的值都是外部声明类型的子类,可以安全接收
- 当容器在生产场景时,容器需要被写入,因此容器内需要使用一个父类引用接收写入值,因此容器泛型使用逆变声明,确保写入值都是容器声明类型的子类,可以安全写入
具体化泛型参数
用于解决获取泛型参数的运行时类型问题
获取泛型的运行时类型场景
- 序列化和反序列化
- 数据库操作
- 自定义注解
- 泛型类型检查
kotlin和java在运行时会进行类型擦除,因此无法在运行时获取泛型的实际类型
- java可以通过反射获取
- kotlin使用reified+inline实现
1
2
3
4
5
6
7
8
9
// inline+reified
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
// T 可以作为类型,用于检查和转换
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
本文由作者按照 CC BY 4.0 进行授权