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