An important difference
between Java and Scala generics is how variance under
inheritance works. For example, if a method has an argument of type
List[AnyRef], can you pass a
List[String] value? In other words, should a
List[String] be considered a
List[AnyRef]? If so,
this kind of variance is called covariance, because
the supertype-subtype relationship of the container (the parameterized
type) “goes in the same direction” as the relationship between the type
parameters. In other contexts, you might want
contravariant or invariant
behavior, which we’ll describe shortly.
In Scala, the variance
behavior is defined at the declaration site using
-, or nothing. In other words, the type designer
decides how the type should vary under inheritance.
Let’s examine the three
kinds of variance, summarized in Table 12-1, and understand how to use
them effectively. We’ll assume that
Tsup is a
Tsub is a
Table 12-1. Type variance annotations and their meanings
Covariant subclassing. E.g.,
Contravariant subclassing. E.g.,
Invariant subclassing. E.g.,
The “Java equivalent” column is a bit misleading; we’ll explain why in a moment.
List[+A], which means that
List[String] is a subclass of
Lists are covariant ...