open class A
class B: A()
fun <T> copy(src: MutableList<T>, dst: MutableList<T>) {
for (i in 0 until src.size) {
dst.add(i, src[i])
}
}
对于上面提到的代码,我理解copy function
期望的两个类型参数完全相同的类型。稍微修改一下copy(src: MutableList<T>, dst: MutableList<in T>)
,注意关键字中的,我是说src
必须是完全类型T
,但目的地可以是type T
或T的任何超类型。
对于上面修改的代码,我可以调用以下方法,
fun main(args: Array<String>) {
val l1 = mutableListOf(B(), B())
val l2 = mutableListOf<A>()
copy(l1, l2)
} // main
如果我从目的地删除in
(已理解(,则上述copy(l1, l2)
不起作用。
我的问题是,如果更新函数参数src
以接受列表的out
投影,我能够在没有任何错误的情况下调用该函数。例如
fun <T> copy(src: MutableList<out /*notice out here*/ T>, dst: MutableList<T>) {
for (i in 0 until src.size) {
dst.add(i, src[i])
}
}
在这种情况下,我无法理解引擎盖下发生了什么。谁能解释一下吗
请注意,这只是书中的一个例子。我知道我可以在src
中使用List
而不是不可变列表
由于您只以一种方式使用函数,因此无论如何都应该使用站点方差修饰符来向调用方明确您可以添加到dst
并从src
:获取数据
fun <T> copy(src: MutableList<out T>, dst: MutableList<in T>) {
for (i in 0 until src.size) {
dst.add(i, src[i])
}
}
此外,由于src
实际上被用作List
而不是MutableList
,因此您应该相应地更喜欢它。因此,您将不再需要out
修饰符,因为List
已经将其类型参数T
定义为仅out
:
fun <T> copy(src: List<T>, dst: MutableList<in T>)
在回答您的问题时:当您调用copy
时,主列表中有两个不同类型的列表,一个是MutableList<A>
,一个则是MutableList<B>
,实际上会出现问题。编译器无法推断copy
的类型是A
还是B
。要解决此问题,您需要提供更多信息:
1( 当您将dst
设置为MutableList<in T>
时,编译器知道您将只向其中添加基于src
的T
类型(在您的示例中,这是B
(。
2( 当您将src
设置为MutableList<out T>
时,编译器会理解您只会将T
及其子类型添加到dst
中,这也很好(在这种情况下,T
将被推断为A
(。
out
此处与in
:对称工作
在关键字中,我的意思是src必须是T类型,但destination可以是T类型或任何超类型的T
所以现在您说src
必须是T
类型的MutableList
或T
的任何子类型,而dst
必须是完全类型为T
的MutableList
。
因此,当您有l1: MutableList<B>
和l2: MutableList<A>
时,编译器将copy(l1, l2)
中的类型参数推断为copy<A>(l1, l2)
,并进行类型检查:MutableList<B>
是MutableList<out A>
的子类型。
因为您在src
上只使用与out
兼容的操作,在dst
上只使用in
兼容的操作。@s1m0nw1说,包含这两个修饰符是完全合理的。