E.g. why does
val list:List[Any] = List[Int](1,2,3)
work, but
val arr:Array[Any] = Array[Int](1,2,3)
fail
The difference is that Lists are immutable while Arrays are mutable.
To understand why mutability determines variance, consider making a mutable version of List - let's call it MutableList. We'll also make use of some example types: a base class Animal and 2 subclasses named Cat and Dog.
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
def jump = // ...
}
class Dog extends Animal {
def makeSound = "bark"
}
Notice that Cat has one more method (jump) than Dog.
Then, define a function that accepts a mutable list of animals and modifies the list:
def mindlessFunc(xs: MutableList[Animal]) = {
xs += new Dog()
}
Now, horrible things will happen if you pass a list of cats into the function:
val cats = MutableList[Cat](cat1, cat2)
val horror = mindlessFunc(cats)
If we were using a careless programming language, this will be ignored during compilation. Nevertheless, our world will not collapse if we only access the list of cats using the following code:
cats.foreach(c => c.makeSound)
But if we do this:
cats.foreach(c => c.jump)
A runtime error will occur. With Scala, writing such code is prevented, because the compiler will complain.