This code works:
@ mutable.Seq(1, 2).asInstanceOf[Seq[Int]]
res1: Seq[Int] = ArrayBuffer(1, 2)
But this doesn't:
@ mutable.Map(1 -> 2).asInstanceOf[Map[Int, Int]]
java.lang.ClassCastException: scala.collection.mutable.HashMap cannot be cast
to scala.collection.immutable.Map
ammonite.$sess.cmd1$.<init>(cmd1.sc:1)
ammonite.$sess.cmd1$.<clinit>(cmd1.sc)
Why can mutable.Seq
be viewed as immutable, but not mutable.Map
? I understand that casting a mutable Seq
to an immutable one is "lying" about the mutability of the underlying collection, but in some situations the programmer knows better—e.g. when returning a collection from a function which uses a mutable list to build up a result, but returns an immutable value.
You can simply create a new HashMap from the existing Map using the copy constructor. HashMap<String, Object> = new HashMap<>(immutableMap);
Thanks to that, we have direct access to a value under a given key. Scala defines two kinds of maps, the immutable, which is used by default and mutable, which needs an import scala. collection.
Generally, from mutable to immutable, you use the to* series methods in mutable collections, like MutableList and ListBuffer's toList method. In the other hand, from immutable to mutable, you can just use constructors like this: scala. collection. mutable.
Mutable maps supports modification operations such as add, remove, and clear on it. Unmodifiable Maps are “read-only” wrappers over other maps. They do not support add, remove, and clear operations, but we can modify their underlying map.
The default Map
is defined in Predef
as
type Map[A, +B] = collection.immutable.Map[A, B]
so it is explicitly immutable
, and mutable.Map
is not a subclass of it.
In contrast to that, the default Seq
is defined directly in scala
as
type Seq[+A] = scala.collection.Seq[A]
so it is a supertype of both mutable.Seq
and immutable.Seq
. This is why your first asInstanceOf
does not fail: every mutable.Seq
is also a collection.Seq
.
As explained here, the decision to not specify whether Seq
has to be mutable or immutable had something to do with support for arrays and varargs.
In 2.13, the default Seq
will become immutable
, and a new type ImmutableArray
will be introduced to deal with varargs
. (Thanks @SethTisue for pointing it out)
The primary problem is this:
If scala.collection.mutable.Map
was a subclass of scala.collection.immutable.Map
, then the former is-a latter too. That is, a mutable Map
is also immutable. Does that make sense?
To illustrate this, you could pass an instance of a mutable Map
to a function or constructor expecting an immutable Map
. Alas, the two types have different semantics: if you, say, add an element to the immutable version, you'll get a new immutable Map
instance returned; yet if you add an element to the mutable version, it changes that instance's contents—thus, it will have a side-effect.
As a consequence, if you wanted to write a pure, referentially transparent (RT) function (i.e. one that has no side-effects) that takes an immutable Map
argument, you couldn't achieve your goal—anyone could screw that up by passing it a mutable Map
instance instead. This would then change the meaning of your code and potentially cause all manner of problems.
In functional programming, immutability is big deal, as is RT. By ensuring that the two cannot be confused, programs that need immutable Map
s can guarantee that they will get them.
(Of course, if you explicitly want to write code that will accept either, you could request an instance of their common scala.collection.Map
trait instead.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With