Search code examples
scalatypescovariancecontravariance

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?

Is it just convenience, so that can write def bar: T rather than def bar: Bar?

In which context is it useful?

Is it actually accurate to say that there is no variance in java? Can one not model it with <? extends Foo> and <? super Bar>?


Solution

  • 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 is usually said to have use-site variance, as opposed to Scala declaration-site variance (well, really Scala supports both). It is actually more expressive, strictly speaking: you can write more sensible programs, e.g. 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. 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.

    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? and https://kotlinlang.org/docs/reference/generics.html#variance.