Everything negative-pressure,challenges-is all an opportunity for me to rise
10 September 2020
这篇文章讨论一下Kotlin中的泛型
在Java中,Collection<String>
是Collection<? extends Object>
的子类,因为该类型发生了协变(covariant),根据PECS原则,此类型作为Producer只能生产数据。对于下面这个类:
1
2
3
interface Producer<T> {
T produce();
}
这个类只有一个方法,它只能生产数据,并不能消耗数据,所以Producer<String>
应该是Producer<Object>
的子类,但Java编译器不知道,而且没有办法向Java编译器解释这种行为,所以Java禁止了它。
但是在Kotlin中,可以向Kotlin编译器解释这种行为,这被称为:declaration-site variance。
我们可以对Producer
的类型参数(type parameter)T进行注释,以确保仅从Producer<T>
的produce方法返回(产生)该参数,并且永不使用它。为此我们提供out
修饰符(variance annotation):
1
2
3
interface Producer<out T> {
fun produce(): T
}
这样,Producer在类型参数T中是协变的,或者T是协变类型参数。我们可以将Producer视为T的生产者,而不是T的消费者。 这样就完成下面转换:
1
2
3
4
5
val stringProducer = object : Producer<String> {
override fun produce(): String = "produce str"
}
val anyProducer: Producer<Any> = stringProducer
相对于out
修饰符使类型参数(type parameter)发生协变(covariant),in
修饰符使类型参数(type parameter)发生逆变(contravariance),我们可以对Consumer
的类型参数(type parameter)T进行in
注释,以确保它只能被消耗而不能产生。这样我们可以将Consumer
视为T的消费者而不是生产者。
1
2
3
interface Consumer<in T> {
fun consume(t: T)
}
这样就完成下面转换:
1
2
3
4
5
6
7
8
val anyConsumer = object : Consumer<Any> {
override fun consume(t: Any) {
require(t is String && t == "string consumer")
}
}
val stringConsumer: Consumer<String> = anyConsumer
stringConsumer.consume("string consumer")
Consumer in, Producer out
Array<out String>
对应Java中的Array<? extends String>
。Array<in String>
对应Java中的Array<? super String>
。上边界(Upper bounds)是泛型限制中最常见的限制类型,泛型默认的上边界是Any?
,并且只能有一个上边界:
<T: Number>
。如果同一个类型参数需要一个以上的上限,则需要单独的where
子句:
1
2
3
4
5
fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it.toString() }
}
传递的类型必须同时满足where子句的所有条件。在上面的示例中,T类型必须同时实现CharSequence和Comparable。
在泛型声明中,Kotlin仅仅在编译时(compile time)执行类型安全检查,而在运行时(runtime),泛型类型的实例不持有类型参数的实际任何信息(类型擦除),比如Comparable<String>
实例在运行时会被擦除成Comparable<*>
。
因此,编译器禁止comparable is Comparable<String>
。
1
Cannot check for instance of erased type: Comparable<String>
并且类型转换(Type casts)在运行时(runtime)是unchecked的:comparable as Comparable<String>
。
1
Unchecked cast: Comparable<T> to Comparable<String>
内联函数的reified
的类型参数被调用位置的内联函数主体中的实际类型参数替换,因此可以用于类型检查和强制转换
1
2
3
4
5
6
7
8
9
10
inline fun <reified T> membersOf() = T::class.members
inline fun <reified T> foo(t: T): Int? {
if (t is String) {
return (t as String).toIntOrNull()
}
if (t is Int) return t
if (t is Int?) return t
return null
}
— Lenox Xian