Jiang Yukun
Jiang Yukun

Reputation: 73

HashMap in scala.collection.mutable is invariant but immutable.HashMap is covariant, why?

I want to extends a class with a val of mutable.HashMap[] like this:

class Father
class Son extends Father    

class C1{
  val m = new mutable.HashMap[Int, Father]() 
}

class C2 extends C1{
  override val m = new mutable.HashMap[Int, Son]()
}

And get an error:

Error:(19, 16) overriding value m in class C1 of type scala.collection.mutable.HashMap[Int,ScalaByExample.Father]; value m has incompatible type override val m = new mutable.HashMapInt, Son

I found that immutable.HashMap is covariant but mutable.HashMap is invariant. It works if replace mutable.HashMap with immutable.HashMap.

So my two questions are:

  1. How can I make it works using mutable.HashMap?

  2. Why did scala's author design HashMap like this?

Upvotes: 5

Views: 427

Answers (2)

Phasmid
Phasmid

Reputation: 953

The reason that immutable collections in Scala are able to be covariant is because if you want to add an element, you will actually be creating a brand new object, with a (potentially) different underlying type. That's to say the underlying type of the new map will be either the same type as in the original or a supertype, as required.

So, in your situation, if you start with an instance of HashMap[Int,Son] and add an object which is a Father, the resulting map will actually be of type HashMap[Int,Father]. All but one of the elements of the new map will actually be Son objects (Son is a subclass of Father). Only the one that you added will actually be a Father. But as far as the compiler is concerned, all it knows about the new map is that all elements are of type Father.

If, as I imagine, the types of your maps are more important to you than the mutability, then you should switch to immutable types. It's rare that you really need mutability in a collection like this.

Upvotes: 1

sepp2k
sepp2k

Reputation: 370162

Mutable maps are invariant because writing to them wouldn't be safe otherwise. Consider for example the following function:

def f(o: C1) {
    o.m(42) = new Father
}

This method is perfectly well-typed. But if you passed in an instance of C2 as the value for o, it'd break because a Map[Int, Son] is not allowed to contain Father objects. Therefore your definition of C2 is ill-typed.

Upvotes: 6

Related Questions