嵌套协方差在 Kotlin 中是如何工作的?

问题描述

这是一段代码,我很难理解为什么第一个编译而第二个不编译?

class Test11<T : Number> {
    lateinit var test: MutableList<out T>.() -> Unit
}

fun main() {
    val test: Test11<Int> = Test11<Int>()
    val test2: Test11<out Number> = test
    test.test.invoke(MutableList(3) { 55 })  // First
    test2.test.invoke(MutableList(3) { 55 })  // Second
}

第二个说 MutableList<nothing> 是预期的。

所以基本上在第一种情况下,T => Int 可能是 out T => out Int => out Number。在第二种情况下,T => out Number 是 Number 的子类,那么仍然 out T => out Number 对吗?

我无法理解为什么它不能按这种逻辑工作...

解决方法

MutableList 是一个函数参数。您会遇到完全相同的问题:

class Test11<T : Number> {
    fun test(list: MutableList<out T>) {

    }
}

fun main() {
    val test: Test11<Number> = Test11<Number>()
    val test2: Test11<out Number> = test
    test.test(MutableList(3) { 55 })  // First
    test2.test(MutableList(3) { 55 })  // Second
}

根据定义,协变类型可防止调用类型为参数的函数,但这在逻辑上也扩展到相同类型的嵌套协变。如果 T 是协变的(对于类),那么使用可以产生 Ts 的对象并不比直接使用 Ts 更安全。

这可能导致失败的示例:

class Test11<T : Number> {
    var list: MutableList<out T>? = null
    fun test(list: MutableList<out T>) {
        this.list = list
    }
}

fun main() {
    val test: Test11<Long> = Test11()
    val test2: Test11<out Number> = test
    val doubleList: MutableList<out Number> = mutableListOf(1.0)
    test2.test(doubleList) // Not allowed
    
    // if it were allowed:
    val long: Long? = test.list?.firstOrNull() // ClassCastException casting the Double to a Long
}