Chris Martin Chris Martin - 3 months ago 12
Scala Question

Unmodifiable view of a mutable Scala collection

I have a class with a private field that is a mutable collection. The field in this particular instance is an

ArrayBuffer
, although my question extends to any finite, ordered, random-access collection type. I want to expose this field without permitting others to modify it. In Java I would add a method like:

private List<T> theList;

public List<T> getList() {
return Collections.unmodifiableList(theList);
}


In Java we just accept that the result is a
List
that doesn't fully implement the
List
interface because
#add
and friends throw
UnsupportedOperationException
.

In Scala, I would expect to find an appropriate trait with accessors like
iterator
,
size
, and
apply
(for retrieving values by index) but no mutators. Does such a type exist that I just haven't found yet?

Answer

The Scala collection library is oriented to immutability, and immutability doesn't refer only to the fact that you are not allowed to modify a given collection, but also that you are guaranteed that the collection won't be ever modified by anyone.

So you cannot and you shouldn't get a collection like immutable.Seq as a view from a mutable buffer in Scala since it breaks that guarantee.

But you can implement the concept of unmodifiable mutable Seq easy enough like so:

class UnmodifiableSeq[A](buffer: mutable.Seq[A]) extends mutable.Seq[A]{
    def update(idx: Int, elem: A) {throw new UnsupportedOperationException()}

    def length = buffer.length

    def apply(idx: Int) = buffer(idx)

    def iterator = buffer.iterator
}

Usage:

val xs = Array(1, 2, 3, 4)
val view = new UnmodifiableSeq(xs)
println(view(2)) >> 3
view(2) = 10 >> Exception in thread "main" java.lang.UnsupportedOperationException

EDIT :

A probably better way of obtaining an unmodifiable view of the collection is by downcasting to collection.Seq which provides no mutable update operations:

val xs = Array(1, 2, 3)
val view: Seq[Int] = xs //this is unmodifiable

or creating a wrapper which extends Seq if you have your own custom mutable class.

class UnmodifiableView[A](col: MutableCollection[A]) extends collection.Seq[A]{
    def length = col.length

    def apply(idx: Int) = col(idx)

    def iterator = col.iterator
}

The scala.collection.Seq trait does not make any guarantees of immutability but it also does not allow any modifying operations so it seems the perfect fit.