19.5. Make Immutable Collections Covariant

Problem

You want to create a collection whose elements can’t be changed (they’re immutable), and want to understand how to specify it.

Solution

You can define a collection of immutable elements as invariant, but your collection will be much more flexible if you declare that your type parameter is covariant. To make a type parameter covariant, declare it with the + symbol, like [+A].

Covariant type parameters are shown in the Scaladoc for immutable collection classes like List, Vector, and Seq:

class List[+T]
class Vector[+A]
trait Seq[+A]

By defining the type parameter to be covariant, you create a situation where the collection can be used in a more flexible manner.

To demonstrate this, modify the example from the previous recipe slightly. First, define the class hierarchy:

trait Animal {
  def speak
}

class Dog(var name: String) extends Animal {
  def speak { println("Dog says woof") }
}

class SuperDog(name: String) extends Dog(name) {
  override def speak { println("I'm a SuperDog") }
}

Next, define a makeDogsSpeak method, but instead of accepting a mutable ArrayBuffer[Dog] as in the previous recipe, accept an immutable Seq[Dog]:

def makeDogsSpeak(dogs: Seq[Dog]) {
  dogs.foreach(_.speak)
}

As with the ArrayBuffer in the previous recipe, you can pass a sequence of type [Dog] into makeDogsSpeak without a problem:

// this works
val dogs = Seq(new Dog("Fido"), new Dog("Tanner"))
makeDogsSpeak(dogs)

However, in this case, you can also pass a Seq[SuperDog] into ...

Get Scala Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.