在Kotlin中使用站点差异


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 TT的任何超类型

对于上面修改的代码,我可以调用以下方法,

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>时,编译器知道您将只向其中添加基于srcT类型(在您的示例中,这是B(。

2( 当您将src设置为MutableList<out T>时,编译器会理解您只会将T及其子类型添加到dst中,这也很好(在这种情况下,T将被推断为A(。

out此处与in:对称工作

在关键字中,我的意思是src必须是T类型,但destination可以是T类型或任何超类型的T

所以现在您说src必须是T类型的MutableListT的任何子类型,而dst必须是完全类型为TMutableList

因此,当您有l1: MutableList<B>l2: MutableList<A>时,编译器将copy(l1, l2)中的类型参数推断为copy<A>(l1, l2),并进行类型检查:MutableList<B>MutableList<out A>的子类型。

因为您在src上只使用与out兼容的操作,在dst上只使用in兼容的操作。@s1m0nw1说,包含这两个修饰符是完全合理的。

相关内容

  • 没有找到相关文章

最新更新