泛型与型变 (Generics & Variance)
Kotlin 的泛型系统比 Java 更加严格且类型安全。核心难点在于理解 协变 (out) 与 逆变 (in)。
基础泛型
kotlin
class Box<T>(val item: T)
fun <T> singletonList(item: T): List<T> {
return listOf(item)
}型变 (Variance):out 与 in
在 Java 中,List<String> 不是 List<Object> 的子类型。这保证了类型安全,但也限制了灵活性。 Kotlin 使用 out 和 in 关键字来解决这个问题。
记忆法则:PECS
Effective Java 提出的 PECS 原则 (Producer-Extends, Consumer-Super) 在 Kotlin 中完美对应:
- Producer -> out: 只能取 (作为返回值),不能存。
- Consumer -> in: 只能存 (作为参数),不能取。
1. 协变 (out) - 生产者
如果你希望 Source<Dog> 是 Source<Animal> 的子类型的,你需要标记 T 为协变。 代价:T 只能出现在返回位置。
kotlin
// 定义:interface List<out E>
interface Source<out T> {
fun nextT(): T // ✅ 作为返回值
// fun add(t: T) // ❌ 编译错误:不能作为参数
}
fun demo(dogs: Source<Dog>) {
val animals: Source<Animal> = dogs // ✅ 子类泛型赋值给父类泛型
}2. 逆变 (in) - 消费者
如果你希望 Comparable<Animal> 是 Comparable<Dog> 的子类型(这是反直觉的,但逻辑正确:能比较所有动物的比较器,一定能比较狗),你需要标记 T 为逆变。 代价:T 只能出现在参数位置。
kotlin
// 定义:interface Comparable<in T>
interface Consumer<in T> {
fun consume(t: T) // ✅ 作为参数
// fun produce(): T // ❌ 编译错误
}
fun demo(animalConsumer: Consumer<Animal>) {
val dogConsumer: Consumer<Dog> = animalConsumer // ✅ 父类泛型赋值给子类泛型
dogConsumer.consume(Dog())
}泛型约束
默认泛型上界是 Any?。
kotlin
// T 必须是非空的
fun <T : Any> checkNotNull(t: T) {}kotlin
// T 必须同时继承 View 并实现 Checkable
fun <T> verify(view: T) where T : View, T : Checkable {
view.isChecked = true // 同时拥有两者的 API
}星号投影 (Star-projection)
不知道(或不关心)具体泛型类型,但想安全使用?使用 *。
- 对于
Foo<out T>:Foo<*>相当于Foo<out TUpper>(只读) - 对于
Foo<in T>:Foo<*>相当于Foo<in Nothing>(只写,也即啥都不能写)
kotlin
fun printList(list: List<*>) {
// 可以读取为 Any?
val item: Any? = list[0]
}类型擦除与 Reified
JVM 运行时会擦除泛型信息。List<String> 在内存中只是 List。 如果你需要在函数体内部使用泛型类型 T(例如 p as T 或 T::class),必须使用 内联函数 + reified。
kotlin
inline fun <reified T> String.toObject(): T {
// Gson 示例:这里能拿到 T 的真实 Class
return Gson().fromJson(this, T::class.java)
}
val user: User = json.toObject()