All Java generics are invariant, because Java uses use-site variance, not declaration-site variance (like Scala does). Therefore you can write:
val listS: scala.List[AnyRef] = scala.List[String]()
but you cannot write:
val listJ: java.util.ArrayList[AnyRef] = new java.util.ArrayList[String]()
the compilation error is:
error: type mismatch;
found : util.this.ArrayList[String]
required: util.this.ArrayList[scala.this.AnyRef]
Note: String <: scala.this.AnyRef, but Java-defined class ArrayList is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: scala.this.AnyRef`. (SLS 3.2.10)
val listJ: java.util.ArrayList[AnyRef] = new java.util.ArrayList[String]()
Indeed, doing what compiler suggest fixes compilation error. Following code compiles:
val listJ: java.util.ArrayList[_ <: AnyRef] = new java.util.ArrayList[String]()
and is equivalent to Java’s code:
java.util.ArrayList<? extends Object> listJ = new java.util.ArrayList<String>();
Note that with <Object>
instead of <? extends Object>
Java code also won’t compile and compiler will output this error:
error: incompatible types: ArrayList<String> cannot be converted to ArrayList<Object>
java.util.ArrayList<Object> listJ = new java.util.ArrayList<String>();
In Scala some collections are covariant and some are invariant. As a rule of thumb:
- immutable collections are covariant, so you can do:
val listImm: collection.immutable.Seq[AnyRef] = collection.immutable.Seq[String]()
- mutable collections are invariant, so you can’t do:
val listMut: collection.mutable.Seq[AnyRef] = collection.mutable.Seq[String]()
More about variances: https://docs.scala-lang.org/tour/variances.html