简体   繁体   English

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

[英]Why do we need variance if we already have type boundaries?

If I write Foo[_ <: Bar] or Foo[+T <: Bar] what does the latter let me do, that I could not do with the former? 如果我写Foo[_ <: Bar]Foo[+T <: Bar]后者让我做什么,我不能用前者做什么?

Is it just convenience, so that can write def bar: T rather than def bar: Bar ? 它只是方便,所以可以写def bar: T而不是def bar: Bar

In which context is it useful? 它在哪种情况下有用?

Is it actually accurate to say that there is no variance in java? 是否真的准确地说java中没有变化? Can one not model it with <? extends Foo> 可以不用<? extends Foo> <? extends Foo> and <? super Bar> <? extends Foo><? super Bar> <? super Bar> ? <? super Bar>

The type bounds control what types are valid parameters. 类型边界控制哪些类型是有效参数。 The covariance/contravariance annotations control the sub/super-typing relationship between instances with different parameters. 协方差/逆变量注释控制具有不同参数的实例之间的子/超类型关系。 Foo[+T] means Foo[A] is a subtype of Foo[B] if A is a subtype of B . Foo[+T]表示Foo[A]是一种亚型Foo[B]如果A是的子类型B Foo[-T] means the reverse. 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

One benefit as opposed to using type boundaries like Foo1[_ <: Bar] everywhere is the compiler will enforce certain properties on the class itself. 与使用Foo1[_ <: Bar]等类型边界相反的一个好处是编译器将在类本身上强制执行某些属性。 For instance, this won't compile: 例如,这不会编译:

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

Neither will this: 这也不会:

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

As far as I know, Java has no way to explicitly represent covariance or contravariance. 据我所知,Java没有办法明确表示协方差或逆变。 This has led to a lot of bugs, especially with arrays being implicitly covariant even though they shouldn't be since they are mutable. 这导致了很多错误,尤其是数组是隐式协变的,即使它们不应该是因为它们是可变的。

Is it actually accurate to say that there is no variance in java? 是否真的准确地说java中没有变化? Can one not model it with <? extends Foo> 可以不用<? extends Foo> <? extends Foo> and <? super Bar> <? extends Foo><? super Bar> <? super Bar> ? <? super Bar>

Java is usually said to have use-site variance, as opposed to Scala declaration-site variance (well, really Scala supports both). 通常认为Java具有使用站点差异,而不是Scala声明站点差异(嗯,实际上Scala支持两者)。 It is actually more expressive, strictly speaking: you can write more sensible programs, eg methods which don't put anything into a List may be covariant while methods which put but don't look at the contents may be contravariant. 它实际上更具表现力,严格来说:你可以编写更明智的程序,例如,不将任何内容放入List方法可能是协变的,而放置但不看内容的方法可能是逆变的。 With declaration-site variance you instead need to have separate immutable and mutable types. 使用声明站点差异,您需要具有单独的不可变和可变类型。

A well-known symptom of the problem is the signature of Set#contains in Scala, which can't just accept A without forcing Set to be invariant. 这个问题的一个众所周知的症状是Scala中Set#contains的签名,它不能只接受A而不强制Set不变。

The problem with having only use-site variance is that it complicates the signatures of all methods working with the type if you want to be consistent: not just the ones declared on the type itself, but those which call them. 只有使用站点方差的问题在于,如果你想要保持一致,它会使使用该类型的所有方法的签名变得复杂:不仅仅是在类型本身上声明的那些,而是那些调用它们的方法。

See also How does Java's use-site variance compare to C#'s declaration site variance? 另请参阅Java的使用站点差异与C#的声明站点差异相比如何? and https://kotlinlang.org/docs/reference/generics.html#variance . https://kotlinlang.org/docs/reference/generics.html#variance

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM