Kotlin泛型
一、泛型
Kotlin 中的类也可以有类型参数:
class Box<T>(t: T) {
var value = t
}
val box1: Box<Int> = Box(1)
//类型参数可以推断出来,例如从构造函数的参数或者从其他途径,允许省略类型参数:
val box2 = Box(1)
二、型变
声明处型变(declaration-site variance)与类型投影(type projections)。
1.java通配符
- 上边界通配限定符,让 Collection<? extends Object>是 Collection<String>的超类
- 下边界通配限定符,让List<? super String> 是 List<Object> 的⼀个超类
2.声明处型变
声明处型变:在类型参数声明处提供。这与 Java 的使用处型变相反。
out 修饰符:标注 Source 的类型参数 T 来确保它仅从 Source<T> 成员中返回(生产),并从不被消费。
interface Source<out T> {
fun nextT(): T
}
fun demo(strs: Source<String>) {
val objects: Source<Any> = strs // 这个没问题,因为 T 是⼀个 out-参数
}
in修饰符:它使得⼀个类型参数逆变:只可以被消费而不可以被生产。逆变类型的⼀个很好的例子是 Comparable :
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
fun demo(x: Comparable<Number>) {
x.compareTo(1.0) // 1.0 拥有类型 Double,它是 Number 的⼦类型
val y: Comparable<Double> = x // 因此,我们可以将 x 赋给类型为 Comparable <Double> 的变量
}
三、类型投影
1.使用处型变:类型投影
val ints: Array<Int> = arrayOf(1, 2, 3)
val any = Array<Any>(3) { "" }
copy(ints, any)
fun copy(from: Array<out Any>, to: Array<Any>) { …… }
类型投影: from 不仅仅是⼀个数组,而是⼀个受限制的(投影的)数组,只可以调用返回类型为类型参数 T 的方法,如上,这意味着我们只能调用 get() 。这就是我们的使用处型变的用法, 并且是对应于 Java 的 Array<? extends Object> ,但使用更简单些的方式。
in 投影⼀个类型:
fun fill(dest: Array<in String>, value: String) { …… }
Array<in String> 对应于 Java 的 Array<? super String> ,也就是说,你可以传递⼀个 CharSequence 数组或⼀个 Object 数组给 fill() 函数。
2.星投影
对类型参数一无所知,但仍然希望以安全的方式使用它。定义泛型类型的投影,该泛型类型的每个具体实例化将是该投影的子类型。 Kotlin 星投影语法:
- 对于 Foo <out T : TUpper> ,其中 T 是⼀个具有上界 TUpper 的协变类型参数,Foo <> 等价于 Foo <out TUpper> 。这意味着当 T 未知时,你可以安全地从 Foo <> 读取 TUpper 的值。
- 对于 Foo <in T> ,其中 T 是⼀个逆变类型参数,Foo <> 等价于 Foo <in Nothing> 。这意味着当 T 未知时,没有什么可以以安全的⽅式写⼊ Foo <> 。
- 对于 Foo <T : TUpper> ,其中 T 是⼀个具有上界 TUpper 的不型变类型参数,Foo<*> 对于读取值 时等价于 Foo<out TUpper> ⽽对于写值时等价于 Foo<in Nothing>
果泛型类型具有多个类型参数,则每个类型参数都可以单独投影: - Function<*, String> 表示 Function<in Nothing, String>
- Function<Int, *> 表示 Function<Int, out Any?>
- Function<*, *> 表示 Function<in Nothing, out Any?> 。
四、泛型函数
不仅类可以有类型参数。函数也可以有。类型参数要放在函数名称之前:
fun <T> singletonList(item: T): List<T> ?{
// ……
}
fun <T> T.basicToString(): String { // 扩展函数
// ……
}
五、泛型约束
能够替换给定类型参数的所有可能类型的集合可以由泛型约束限制。
1.上界
约束类型是与 Java 的 extends 关键字对应的 上界,冒号之后指定的类型是上界:只有 Comparable<T> 的⼦类型可以替代 T
fun <T : Comparable<T>> sort(list: List<T>) { …… }
默认的上界(如果没有声明)是 Any? 。在尖括号中只能指定⼀个上界。如果同⼀类型参数需要多个上界,我们需 要⼀个单独的 where-子句:
fun <T> copyWhenGreater(
list: List<T>,
threshold: T
): List<String> where T : CharSequence, T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
六、类型擦除
Kotlin 为泛型声明用法执行的类型安全检测仅在编译期进行。运行时泛型类型的实例不保留关于其类型实参的任何信息。其类型信息称为被擦除。例如,Foo<Bar> 与 Foo<Baz?> 的实例都会被擦除为 Foo<*>