I am confused by the generic subtyping.
In Java, if type A
is a subtype of B
, generic type C<A>
and C<B>
are invariant. For instance, ArrayList<Base>
is not a subtype of ArrayList<Derived>
.
However, in Scala, generic type C<A>
and C<B>
are covariant if type A
is a subtype of B
. So what's the property of generic class in Scala has but not in Java?
Best Answer
Firstly note that variance is a property of generic type parameters, not of the parameterized types themselves.
Secondly: you are wrong about scala - type parameters are invariant by default. Let us investigate!
Java
Java has use-site variance annotations. That is, you can declare methods like this:
There is, however, one form of "parameterized type" (I use the term loosely) in which the type parameters are covariant: Java Arrays! (This is actually insane because Arrays are mutable and hence it is easy to circumvent the type system). Consider the following:
And then:
A good interview question this one: what happens?
Scala
In Scala, you have declaration-site variance. That is, the variance of a type parameter is declared alongside the parameter (using the annotations
+
and-
):This says that the trait
Function1
has two type parameters,I
andO
which are contra- and co-variant respectively. If no+/-
annotation is declared, then the type parameter is invariant. For example, Set is invariant in its type parameter:List is however, declared as being covariant:
Why not ask the compiler?
Another way of demonstrating this is to ask the compiler directly for subtype-evidence:
But with an invariant type parameter
Lastly, contravariance:
See also identifier
<:<