3
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>)一下in关键字,我是说它src 必须是完全类型T,但 destination 可以是 T 的type T任何超类型

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

fun main(args: Array<String>) {
    val l1 = mutableListOf(B(), B())
    val l2 = mutableListOf<A>()
    copy(l1, l2)
} // main

如果我从目的地移除(理解),上述copy(l1, l2)方法不起作用。in

我的问题是,我可以毫无错误地调用该函数我的问题是,如果更新函数参数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])
    }
}

在这种情况下,我无法理解引擎盖下发生了什么。有人可以解释一下吗?

请注意,这只是书中的一个示例。我知道我可以使用List而不是不可变列表src

4

2 回答 2

6

由于您仅以一种方式使用该函数,因此无论如何您都应该使用使用站点方差修饰符,以使调用者清楚您可以添加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确实用作 aList而不是 a MutableList,因此您应该相应地更喜欢它。结果,您将不再需要out修饰符,因为List已经将其类型参数定义Tout

fun <T> copy(src: List<T>, dst: MutableList<in T>)

回答您的问题:当您copy在主目录中使用两个不同类型的列表进行调用时,问题实际上会发生,一次是 with MutableList<A>,一次是 with MutableList<B>。编译器无法推断出的类型copy应该是A还是B。要解决此问题,您需要提供更多信息:

1)当您设置dstMutableList<in T>时,编译器知道您只会添加T基于src它的类型(在您的示例中为B)。

2)当您设置srcMutableList<out T>时,编译器会理解您只会添加T它的子类型以及它的子类型dst(在这种情况下T将被推断为A好像)。

于 2018-11-05T10:01:36.827 回答
1

out这里对称地工作in

在关键字中,我是说 src 必须是 T 类型,但目标可以是 T 类型或 T 的任何超类型

所以现在你说那src必须是 aMutableList的类型T或任何子类型T,而dst必须是 aMutableList的完全类型T

因此,当您拥有l1: MutableList<B>and时l2: MutableList<A>,编译器会推断copy(l1, l2)as中的类型参数copy<A>(l1, l2),并且它MutableList<B>类型检查:是MutableList<out A>.

因为您只在 上使用out-compatible 操作src,并且只在 上使用 -compatiblein操作dst,正如@s1m0nw1 所说,包含这两个修饰符非常有意义。

于 2018-11-05T10:10:30.843 回答