score:4

Accepted answer

Just give the compiler a little help:

case class AnimalsMap[A <: Animal](map:Map[String,A] = Map[String, A]())
                                                          ^^^^^^^^^^^

I'll leave the details of why your solution doesn't work to someone better acquainted with Scala's type inferenceā€¦

Edit: See IttayD's answer for a nice explanation of this behavior.

score:21

Scala has a feature where you can define a class to be covariant / contravariant in its generic parameters.

As an example to covariance: it is natural to think that if class Student extends Person then List[Student] "extends" List[Person]. This is because every method that accepts a List[Person] should have no problem working with an object List[Student]. This is not possible in Java (without making the method also generic).

Contravariance is the opposite and is a bit trickier to explain. It is required when the type is supposed to be pushed to the generic class, not read (in List[Person] you read the elements of the list). The general example is a function. The types of the arguments to a function are put into it, so if a method expects a function Person => String it can't be called with a function Student => String (it will call the argument with a person, but it expects a student)

Scala also defines Nothing to implicitly extend everything. It is the bottom type. So List[Nothing] always "extends" List[X] for any X. List() creates List[Nothing] and the covariance is why you can write val x: List[Person] = List().

Anyways, Map is invariant in its key types. The reason is that a Map[A, B] is like a function A => B so it can only be contravariant in A. Another way is to think what happens if you pass a Map[Student, String] to a method expecting Map[Person, String], obviously it may try to put Person objects in there which is not good, the other way is OK. On the other hand, a Map can be viewed as Iterable[(A, B)], here it should be covariant in A. So it is invariant in its value.

The result is you cannot assign a Map[Nothing, Nothing] to a variable of type Map[String, Animal]. Map() creates a Map[Nothing, Nothing]

The compiler tells you this:

scala> val dogs3 = Map3[Dog]()
<console>:13: error: type mismatch;
 found   : scala.collection.immutable.Map[Nothing,Nothing]
 required: Map[String,Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
       val dogs3 = Map3[Dog]()
                       ^

Related Query