Kotlin 中的使用位置差异
Use-Site variance in 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>)
注意 in 关键字,我是说 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])
}
}
在这种情况下,我无法理解hood.Can下发生了什么,请解释一下?
请注意,这只是书中的一个例子。我知道我可以使用 List
而不是 src
中的不可变列表
由于您仅以一种方式使用该函数,因此您无论如何都应该使用 use-site variance modifiers 以使调用者清楚地知道您可以添加到 dst
并从 [=13] 获取数据=]:
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
:
对称
in keyword, I am saying that src must be of exactly type T but destination can be of type T or any super type of T
所以现在你说 src
必须是 T
类型的 MutableList
或 T
的任何子类型,而 dst
必须是MutableList
类型 T
.
所以当你有 l1: MutableList<B>
和 l2: MutableList<A>
时,编译器将 copy(l1, l2)
中的类型参数推断为 copy<A>(l1, l2)
,并进行类型检查: MutableList<B>
是MutableList<out A>
.
的子类型
因为您只在 src
上使用 out
兼容的操作,并且在 dst
上只使用 in
兼容的操作,正如@s1m0nw1 所说的那样非常有意义包括两个修饰符。
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
但目的地可以是 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])
}
}
在这种情况下,我无法理解hood.Can下发生了什么,请解释一下?
请注意,这只是书中的一个例子。我知道我可以使用 List
而不是 src
由于您仅以一种方式使用该函数,因此您无论如何都应该使用 use-site variance modifiers 以使调用者清楚地知道您可以添加到 dst
并从 [=13] 获取数据=]:
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
:
in keyword, I am saying that src must be of exactly type T but destination can be of type T or any super type of T
所以现在你说 src
必须是 T
类型的 MutableList
或 T
的任何子类型,而 dst
必须是MutableList
类型 T
.
所以当你有 l1: MutableList<B>
和 l2: MutableList<A>
时,编译器将 copy(l1, l2)
中的类型参数推断为 copy<A>(l1, l2)
,并进行类型检查: MutableList<B>
是MutableList<out A>
.
因为您只在 src
上使用 out
兼容的操作,并且在 dst
上只使用 in
兼容的操作,正如@s1m0nw1 所说的那样非常有意义包括两个修饰符。