如果我们已经有了类型边界,为什么还需要方差?
Why do we need variance if we already have type boundaries?
如果我写 Foo[_ <: Bar]
或 Foo[+T <: Bar]
后者让我做什么,而我不能用前者做?
是不是为了方便,所以可以写def bar: T
而不是def bar: Bar
?
它在什么情况下有用?
说 java 没有方差真的准确吗?不能用 <? extends Foo>
和 <? super Bar>
建模吗?
类型边界控制哪些类型是有效参数。
covariance/contravariance注解控制不同参数实例之间的sub/super-typing关系。 Foo[+T]
表示 Foo[A]
是 Foo[B]
的子类型,如果 A
是 B
的子类型。 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 本身上强制执行某些属性。例如,这不会编译:
class Foo[+T]() {
def f(t: T): Unit = {}
}
这也不会:
class Foo[-T]() {
def f(): T = { ??? }
}
据我所知,Java没有办法显式表示协变或逆变。这导致了很多错误,尤其是数组是隐式协变的,尽管它们不应该是可变的,因为它们是可变的。
Is it actually accurate to say that there is no variance in java? Can one not model it with <? extends Foo>
and <? super Bar>
?
Java 通常被认为具有使用位置差异,而不是 Scala 声明位置差异(好吧,Scala 确实支持两者)。严格来说,它实际上更具表现力:您可以编写更明智的程序,例如不将任何内容放入 List
的方法可能是协变的,而放入但不查看内容的方法可能是逆变的。使用声明站点差异,您需要具有单独的不可变和可变类型。
该问题的一个众所周知的症状是 Scala 中 Set#contains
的签名,它不能在不强制 Set
不变的情况下只接受 A
。
只有 使用点差异的问题在于,如果你想保持一致,它会使使用该类型的所有方法的签名复杂化:不仅仅是声明的那些类型本身,但那些调用它们的人。
另见 How does Java's use-site variance compare to C#'s declaration site variance? and https://kotlinlang.org/docs/reference/generics.html#variance。
如果我写 Foo[_ <: Bar]
或 Foo[+T <: Bar]
后者让我做什么,而我不能用前者做?
是不是为了方便,所以可以写def bar: T
而不是def bar: Bar
?
它在什么情况下有用?
说 java 没有方差真的准确吗?不能用 <? extends Foo>
和 <? super Bar>
建模吗?
类型边界控制哪些类型是有效参数。
covariance/contravariance注解控制不同参数实例之间的sub/super-typing关系。 Foo[+T]
表示 Foo[A]
是 Foo[B]
的子类型,如果 A
是 B
的子类型。 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 本身上强制执行某些属性。例如,这不会编译:
class Foo[+T]() {
def f(t: T): Unit = {}
}
这也不会:
class Foo[-T]() {
def f(): T = { ??? }
}
据我所知,Java没有办法显式表示协变或逆变。这导致了很多错误,尤其是数组是隐式协变的,尽管它们不应该是可变的,因为它们是可变的。
Is it actually accurate to say that there is no variance in java? Can one not model it with
<? extends Foo>
and<? super Bar>
?
Java 通常被认为具有使用位置差异,而不是 Scala 声明位置差异(好吧,Scala 确实支持两者)。严格来说,它实际上更具表现力:您可以编写更明智的程序,例如不将任何内容放入 List
的方法可能是协变的,而放入但不查看内容的方法可能是逆变的。使用声明站点差异,您需要具有单独的不可变和可变类型。
该问题的一个众所周知的症状是 Scala 中 Set#contains
的签名,它不能在不强制 Set
不变的情况下只接受 A
。
只有 使用点差异的问题在于,如果你想保持一致,它会使使用该类型的所有方法的签名复杂化:不仅仅是声明的那些类型本身,但那些调用它们的人。
另见 How does Java's use-site variance compare to C#'s declaration site variance? and https://kotlinlang.org/docs/reference/generics.html#variance。