在以下源代码中
fun main(args: Array<String>) {
println("Hello, world!")
val mutableIntList = mutableListOf(1, 2, 3)
addInt(4, mutableIntList) // No compile-time error
addAnotherInt(5, mutableIntList) // Compile-time error
println(mutableIntList)
}
fun <T: Number> addInt(item:T,
list:MutableList<in T>){
list.add(item)
}
fun <T: Number> addAnotherInt(item:T,
list:MutableList<in Number>){
list.add(item)
}
函数addInt
和addAnotherInt
将Number
的逆变MutableList
作为参数。但是在main
函数中,一行正常编译,另一行则不正常。
我还检查了从这些函数生成的 java 代码,它们看起来是相同的。
addInt
和addAnotherInt
函数之间有什么区别?
in Number
的意思是"Number
或其超类型"。 Int
不是"Number
或其超型",而是它的子型。
简而言之,您声明您的addAnotherInt()
想要一个至少与接受任何类型的Number
一样通用的列表。
相比之下,addInt
声明item: T
和list: MutableList<in T>
。 T
本身被声明为函数的自由类型变量,这意味着它将绑定在每个特定的调用站点。所以当你说
addInt(4, mutableIntList)
Kotlin 基于第一个参数将T
绑定到Int
,并将其传播到第二个参数,现在已MutableList<in Int>
。您传入了与该类型兼容的MutableList<Int>
,因此 Kotlin 感到满意。
如果您声明
val mutableIntList: MutableList<Number> = mutableListOf(1, 2, 3)
然后代码将编译,因为现在列表根据需要通用,您可以向其添加任何Number
。
您的代码将使用数字列表进行编译:
val mutableIntList = mutableListOf<Number>(1, 2, 3)
但是由于类型被推断为MutableList<Int>
,所以不能将其用作MutableList<in Number>
。这转换为 Java 等效MutableList<? super Number>
,意味着您可以将任何Number
添加到列表中。但是不可能在MutableList<Int>
中添加Long
。
您的第二种方法addInt()
更严格一些,可以在您的用例中转换MutableList<? super Int>
。因此,您可以这样使用它。不过,这两种方法都能够使用MutableList<Number>
。