1. 泛型基础
泛型本质上是参数化类型。它让我们能够编写可以处理不同类型数据的代码,同时保持类型安全。
1 | class Box<T>(t: T) { |
2. 型变 (Variance):out 与 in
这是 Kotlin 泛型中最难理解的部分。它的核心是为了解决:**List<String> 是否是 List<Any> 的子类?**
2.1 协变 (Covariance):out
- 定义:如果
String是Any的子类,那么Producer<String>也是Producer<Any>的子类。 - 限制:只能从对象中读取(Produce),不能写入(Consume)。
- 关键字:
out
1 | // 声明处型变 |
2.2 逆变 (Contravariance):in
- 定义:如果
String是Any的子类,那么Consumer<Any>反而是Consumer<String>的子类。 - 限制:只能向对象中写入(Consume),不能读取(Produce)。
- 关键字:
in
1 | interface Consumer<in T> { |
2.3 为什么需要型变?
Java 的泛型是不型变的(Invariant)。在 Java 中,List<Object> list = new ArrayList<String>(); 是不允许的。Kotlin 通过 out 和 in 优雅地解决了生产与消费场景下的类型兼容问题。
3. 类型擦除与实化类型参数 (reified)
3.1 类型擦除
在运行时,泛型信息会被擦除。这意味着你不能在运行时直接判断一个对象的泛型类型:
1 | // 错误示例 |
3.2 reified 关键字
Kotlin 引入了 inline 函数配合 reified 关键字,使得我们可以在运行时访问泛型类型。
1 | inline fun <reified T> isType(value: Any): Boolean { |
4. 泛型约束 (Upper Bounds)
我们可以通过 : 来限制泛型必须是某个类的子类。
1 | // T 必须是 Number 或其子类 |
5. 星投影 (Star-projections) *
当你不知道或者不关心泛型的具体类型时,可以使用 *。
List<*>代表“我不知道这里面是什么类型,但我知道它一定是Any?的子类”。- 它是只读的(类似
out Any?)。
总结
- **
out(协变)**:生产者,子类到父类,只读。 - **
in(逆变)**:消费者,父类到子类,只写。 - **
reified**:打破类型擦除,运行时获取类型信息。 - **
:**:类型约束。