Kotlin 协变与逆变 #
一、型变概述 #
型变描述了泛型类型之间的子类型关系。
1.1 问题引入 #
kotlin
class Box<T>(val value: T)
val intBox: Box<Int> = Box(10)
// val numberBox: Box<Number> = intBox // 编译错误!
为什么 Box<Int> 不是 Box<Number> 的子类型?
二、不变 #
2.1 定义 #
默认情况下,泛型是不变的:
kotlin
class Box<T>(var value: T)
// Box<Int> 和 Box<Number> 没有任何关系
2.2 为什么不变 #
kotlin
class Box<T>(var value: T) {
fun get(): T = value
fun set(value: T) { this.value = value }
}
// 如果 Box<Int> 是 Box<Number> 的子类型:
val intBox: Box<Int> = Box(10)
val numberBox: Box<Number> = intBox // 假设允许
numberBox.set(10.5) // 问题:Box<Int> 存入了 Double!
三、协变 #
3.1 定义 #
使用 out 关键字声明协变:
kotlin
interface Producer<out T> {
fun produce(): T
}
3.2 协变规则 #
协变类型只能作为返回值(生产者):
kotlin
interface Producer<out T> {
fun produce(): T // 正确:作为返回值
// fun consume(value: T) // 错误:不能作为参数
}
3.3 子类型关系 #
kotlin
interface Producer<out T> {
fun produce(): T
}
class NumberProducer : Producer<Number> {
override fun produce(): Number = 10
}
fun main() {
val numberProducer: Producer<Number> = NumberProducer()
val anyProducer: Producer<Any> = numberProducer // 正确!
}
3.4 协变示例 #
kotlin
// Kotlin 标准库中的 List 是协变的
interface List<out E> : Collection<E> {
fun get(index: Int): E
// 没有 add(E) 方法
}
val ints: List<Int> = listOf(1, 2, 3)
val numbers: List<Number> = ints // 正确!
四、逆变 #
4.1 定义 #
使用 in 关键字声明逆变:
kotlin
interface Consumer<in T> {
fun consume(value: T)
}
4.2 逆变规则 #
逆变类型只能作为参数(消费者):
kotlin
interface Consumer<in T> {
fun consume(value: T) // 正确:作为参数
// fun produce(): T // 错误:不能作为返回值
}
4.3 子类型关系 #
kotlin
interface Consumer<in T> {
fun consume(value: T)
}
class NumberConsumer : Consumer<Number> {
override fun consume(value: Number) {
println(value)
}
}
fun main() {
val numberConsumer: Consumer<Number> = NumberConsumer()
val intConsumer: Consumer<Int> = numberConsumer // 正确!
}
4.4 逆变示例 #
kotlin
// Comparable 是逆变的
interface Comparable<in T> {
fun compareTo(other: T): Int
}
class NumberComparator : Comparable<Number> {
override fun compareTo(other: Number): Int = 0
}
fun main() {
val numberComparator: Comparable<Number> = NumberComparator()
val intComparator: Comparable<Int> = numberComparator // 正确!
}
五、型变总结 #
5.1 关系图 #
text
协变: Producer<out T>
Producer<Int> 是 Producer<Number> 的子类型
逆变: Consumer<in T>
Consumer<Number> 是 Consumer<Int> 的子类型
不变: Box
Box<Int> 和 Box<Number> 没有关系
5.2 选择指南 #
| 场景 | 型变 | 关键字 |
|---|---|---|
| 只读(生产者) | 协变 | out |
| 只写(消费者) | 逆变 | in |
| 读写 | 不变 | 无 |
六、型变位置 #
6.1 out 位置 #
kotlin
interface Producer<out T> {
fun produce(): T // 返回值位置
val value: T // 属性 getter
fun process(): List<T> // 泛型返回值
}
6.2 in 位置 #
kotlin
interface Consumer<in T> {
fun consume(value: T) // 参数位置
var value: T // 属性 setter(需要 var)
fun process(list: List<T>) // 泛型参数
}
6.3 混合使用 #
kotlin
// 使用类型投影
fun <T> copy(source: Array<out T>, destination: Array<in T>) {
for (i in source.indices) {
destination[i] = source[i]
}
}
七、星投影 #
7.1 基本用法 #
kotlin
fun printList(list: List<*>) {
list.forEach { println(it) }
}
7.2 星投影规则 #
kotlin
// 对于 Producer<out T>:
// Producer<*> 等价于 Producer<out Any?>
val producer: Producer<*> = Producer<Int>()
val value: Any? = producer.produce() // 可以读取
// 对于 Consumer<in T>:
// Consumer<*> 等价于 Consumer<in Nothing>
val consumer: Consumer<*> = Consumer<Number>()
// consumer.consume(value) // 错误:不能写入任何值
7.3 使用场景 #
kotlin
// 当不关心具体类型时
fun getSize(collection: Collection<*>): Int {
return collection.size
}
// 当只需要读取时
fun printElements(list: List<*>) {
list.forEach { println(it) }
}
八、类型投影 #
8.1 out 投影 #
kotlin
fun <T> copy(from: Array<out T>, to: Array<T>) {
for (i in from.indices) {
to[i] = from[i]
}
}
val ints = arrayOf(1, 2, 3)
val numbers = arrayOfNulls<Number>(3)
copy(ints, numbers) // 正确
8.2 in 投影 #
kotlin
fun fill(array: Array<in Number>, value: Number) {
for (i in array.indices) {
array[i] = value
}
}
val numbers: Array<Number> = arrayOf(1, 2, 3)
fill(numbers, 10.5) // 正确
val anys: Array<Any> = arrayOf("", "", "")
fill(anys, 10) // 正确
九、实战示例 #
9.1 响应式流 #
kotlin
interface Subscriber<in T> {
fun onNext(value: T)
fun onError(error: Throwable)
fun onComplete()
}
interface Publisher<out T> {
fun subscribe(subscriber: Subscriber<in T>)
}
9.2 集合操作 #
kotlin
fun <T> List<T>.addAll(elements: Collection<out T>): Boolean {
// elements 是协变的,可以安全读取
return elements.all { this.add(it) }
}
9.3 比较器 #
kotlin
interface Comparator<in T> {
fun compare(a: T, b: T): Int
}
fun <T> List<T>.sortedWith(comparator: Comparator<in T>): List<T> {
// comparator 是逆变的,可以安全写入
return this.sortedWith { a, b -> comparator.compare(a, b) }
}
十、最佳实践 #
10.1 遵循 PECS 原则 #
Producer Extends, Consumer Super:
kotlin
// 生产者(只读)→ out
fun <T> copy(source: List<out T>): List<T>
// 消费者(只写)→ in
fun <T> fill(destination: Array<in T>, value: T)
10.2 优先使用不变 #
kotlin
// 如果同时读写,使用不变
class Box<T>(var value: T)
10.3 使用星投影简化 #
kotlin
// 当类型不重要时
fun printSize(list: List<*>) = println(list.size)
十一、总结 #
型变要点:
| 型变 | 关键字 | 方向 | 场景 |
|---|---|---|---|
| 协变 | out | 子类型保留 | 生产者 |
| 逆变 | in | 子类型反转 | 消费者 |
| 不变 | 无 | 无关系 | 读写 |
| 星投影 | * | 未知类型 | 泛型通配 |
恭喜你完成了 Kotlin 完全指南的学习!
最后更新:2026-03-27