英文:
Kotlin Generic declaration-site variance with use-site variance
问题
class X<out T> {
// Type parameter T is declared as 'out' but occurs in 'in' position in type X<T>
fun doSomethingX(x: X<T>) { }
}
class Y<in T> {
// Type parameter T is declared as 'in' but occurs in 'out' position in type Y<T>
fun doSomethingY(y: Y<T>) { }
}
我理解doSomethingX方法的x参数在类型参数T的位置上使用了'in',因此,X类的错误是合理的。
然而,我不明白为什么doSomethingY方法会导致错误...
有人知道错误的原因吗?
我阅读了Kotlin文档,https://kotlinlang.org/docs/generics.html#declaration-site-variance,但没有得到足够的答案。
另外,我发现了类似的现象。
fun main() {
X<Number>(1).doSomething(X<Int>(2))
X<Int>(1).doSomething(X<Number>(2))
}
class X<out|in T>(private val t: T)
fun <R> X<R>.doSomething(x: X<R>) {
println(this)
println(x)
}
如果类X具有声明点的协变性或逆变性,主函数将不会出现编译错误。从这个现象来看,我猜测声明点泛型类的参数会同时被视为'in'和'out'位置。
但为什么?为什么Kotlin会操作这样呢?
英文:
class X<out T> {
// Type parameter T is declared as 'out' but occurs in 'in' position in type X<T>
fun doSomethingX(x: X<T>) { }
}
class Y<in T> {
// Type parameter T is declared as 'in' but occurs in 'out' position in type Y<T>
fun doSomethingY(y: Y<T>) { }
}
I understood doSomethingX has x parameter with 'in' position (having parameter with type parameter means 'in' position), therefore, error of class X makes sense.
However, I can't understand why doSomethingY casuses error...
Does anyone know the reason for the error?
I read kotlin docs, https://kotlinlang.org/docs/generics.html#declaration-site-variance, but couldn't get enough answers.
Added.
Similarly, I found this phenomenon.
fun main() {
X<Number>(1).doSomething(X<Int>(2))
X<Int>(1).doSomething(X<Number>(2))
}
class X<out|in T>(private val t: T)
fun <R> X<R>.doSomething(x: X<R>) {
println(this)
println(x)
}
Main function does not have compile error if class X has any declaration-site vairance.
From this phenomenon, I guess the parameter of generic class in declaration-site generic class regards as both 'in' and 'out' position.
But why?! Why Kotlin operates like this?
答案1
得分: 0
因为Liskov替代原则,你可以将指定类型的子类型作为函数参数传递。由于Y
具有逆变的in
泛型,Y
的任何类型为T
的超类型的实例都是Y<T>
的子类型。
当函数消耗一个参数时,它可以安全地消耗其子类型。该参数位于“in”位置。
但是如果你的函数试图“消耗”一个Y<T>
,它试图消耗的是其泛型类型可能是T
的超类型,这是“out”位置。
换句话说,函数参数Y
的子类型可以包装T
的超类型。如果输入的T
可以是超类型,那么它与你可以安全地转换函数参数的方向相反。
英文:
Because of the Liskov substitution principle, you are allowed to pass a subtype of a specified type as an argument to the function. Since Y
has a contravariant in
generic, any instance of Y
whose type is a supertype of T
is a subtype of Y<T>
.
When a function consumes a parameter, it can safely consume subtypes of it. The parameter is in "in" position.
But if your function tries to "consume" a Y<T>
, it is trying to consume something whose generic type could be a supertype of T
, which is "out" position.
In other words, subtypes of the function parameter Y
can wrap supertypes of T
. If the input T
can be a supertype, it is in opposition to the direction you can safely cast a function parameter.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论