Kotlin 声明处型变与使用处型变

huangapple go评论87阅读模式
英文:

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&lt;out T&gt; {
  
  // Type parameter T is declared as &#39;out&#39; but occurs in &#39;in&#39; position in type X&lt;T&gt;
  fun doSomethingX(x: X&lt;T&gt;) { }
  
}

class Y&lt;in T&gt; {
  
  // Type parameter T is declared as &#39;in&#39; but occurs in &#39;out&#39; position in type Y&lt;T&gt;
  fun doSomethingY(y: Y&lt;T&gt;) { }
  
}

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&lt;Number&gt;(1).doSomething(X&lt;Int&gt;(2))
  X&lt;Int&gt;(1).doSomething(X&lt;Number&gt;(2))
}

class X&lt;out|in T&gt;(private val t: T)

fun &lt;R&gt; X&lt;R&gt;.doSomething(x: X&lt;R&gt;) {
  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&lt;T&gt;.

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&lt;T&gt;, 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.

huangapple
  • 本文由 发表于 2023年3月7日 22:03:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662997.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定