如果我们已经有类型边界,为什么我们需要方差?



如果我写Foo[_ <: Bar]Foo[+T <: Bar]后者让我做什么,而前者我不能做什么?

只是方便,所以可以写def bar: T而不是def bar: Bar吗?

它在什么情况下有用?

说 java 中没有差异真的准确吗?不能用<? extends Foo><? super Bar>来建模吗?

类型边界控制哪些类型是有效参数。 协方差/逆变注释控制具有不同参数的实例之间的子/超类型关系。Foo[+T]表示Foo[A]Foo[B]的子类型,如果AB的子类型。Foo[-T]的意思是相反的。

class Foo1[T <: Bar]()
class Foo2[+T <: Bar]()
trait Bar
trait Baz extends Bar
val f1: Foo1[Bar] = new Foo1[Baz]() // compile error
val f2: Foo2[Bar] = new Foo2[Baz]() // works just fine

与在任何地方使用Foo1[_ <: Bar]等类型边界相反的一个好处是编译器将对类本身强制实施某些属性。例如,这不会编译:

class Foo[+T]() {
def f(t: T): Unit = {}
}

这也不会:

class Foo[-T]() {
def f(): T = { ??? }
}

据我所知,Java没有办法明确表示协方差或逆变。这导致了很多错误,特别是当数组隐式协变时,即使它们不应该是可变的,因为它们是可变的。

说 java 中没有方差真的准确吗?不能用<? extends Foo><? super Bar>建模吗?

Java通常被认为具有使用站点差异,而不是Scala声明站点差异(好吧,实际上Scala支持两者(。严格来说,它实际上更具表现力:你可以编写更明智的程序,例如,不将任何东西放入List中的方法可能是协变的,而放入但不查看内容的方法可能是逆变的。对于声明站点差异,您需要具有单独的不可变和可变类型。

这个问题的一个众所周知的症状是 Scala 中Set#contains的签名,它不能只接受A而不强制Set不变。

只有使用站点方差的问题在于,如果你想保持一致,它会使使用该类型的所有方法的签名复杂化:不仅仅是在类型本身上声明的方法,还有那些调用它们的方法。

另请参阅Java的使用站点差异与C#的声明站点差异相比如何?和 https://kotlinlang.org/docs/reference/generics.html#variance。

相关内容

  • 没有找到相关文章

最新更新