How to create Scala classes that use generic types (cookbook examples)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 19.1, “How to create Scala classes that use generic types (cookbook examples).”

Problem

You want to create a Scala class (and associated methods) that uses a generic type.

Solution

As a library writer, creating a class (and methods) that takes a generic type is similar to Java. For instance, if Scala didn’t have a linked-list class and you wanted to write your own, you could write the basic functionality like this:

class LinkedList[A] {

    private class Node[A] (elem: A) {
        var next: Node[A] = _
        override def toString = elem.toString
    }

    private var head: Node[A] = _

    def add(elem: A) {
        val n = new Node(elem)
        n.next = head
        head = n
    }

    private def printNodes(n: Node[A]) {
        if (n != null) {
            println(n)
            printNodes(n.next)
        }
    }

    def printAll() { printNodes(head) }

}

Notice how the generic type A is sprinkled throughout the class definition. This is similar to Java, but Scala uses [A] everywhere, instead of <T> as Java does. (More on the characters A versus T shortly.)

To create a list of integers with this class, first create an instance of it, declaring its type as Int:

val ints = new LinkedList[Int]()

Then populate it with Int values:

ints.add(1)
ints.add(2)

Because the class uses a generic type, you can also create a LinkedList of String:

val strings = new LinkedList[String]()
strings.add("Nacho")
strings.add("Libre")
strings.printAll()

Or any other type you want to use:

val doubles = new LinkedList[Double]()
val frogs = new LinkedList[Frog]()

At this basic level, creating a generic class in Scala is just like creating a generic class in Java, with the exception of the brackets.

Discussion

When using generics like this, the container can take subtypes of the base type you specify in your code. For instance, given this class hierarchy:

trait Animal
class Dog extends Animal { override def toString = "Dog" }
class SuperDog extends Dog { override def toString = "SuperDog" }
class FunnyDog extends Dog { override def toString = "FunnyDog" }

you can define a LinkedList that holds Dog instances:

val dogs = new LinkedList[Dog]

You can then add Dog subtypes to the list:

val fido = new Dog
val wonderDog = new SuperDog
val scooby = new FunnyDog

dogs.add(fido)
dogs.add(wonderDog)
dogs.add(scooby)

So far, so good: you can add Dog subtypes to a LinkedList[Dog]. Where you might run into a problem is when you define a method like this:

def printDogTypes(dogs: LinkedList[Dog]) {
    dogs.printAll()
}

You can pass your current dogs instance into this method, but you won’t be able to pass the following superDogs collection into makeDogsSpeak:

val superDogs = new LinkedList[SuperDog]
superDogs.add(wonderDog)

// error: this line won't compile
printDogTypes(superDogs)

The last line won’t compile because (a) makeDogsSpeak wants a LinkedList[Dog], (b) LinkedList elements are mutable, and (c) superDogs is a LinkedList[SuperDog]. This creates a conflict the compiler can’t resolve. This situation is discussed in detail in Recipe 19.5, “Make Immutable Collections Covariant”.

In Scala 2.10, the compiler is even nice enough to tell you what’s wrong in this situation, and points you toward a solution:

[error] Note: SuperDog <: Dog, but class LinkedList is invariant in type A.
[error] You may wish to define A as +A instead. (SLS 4.5)

Type parameter symbols (naming conventions)

If a class requires more than one type parameter, use the symbols shown in Table 19-3. For instance, in the official Java Generics documentation, Oracle shows an interface named Pair, which takes two types:

// from http://docs.oracle.com/javase/tutorial/java/generics/types.html
public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

You can port that interface to a Scala trait, as follows:

trait Pair[A, B] {
    def getKey: A
    def getValue: B
}

If you were to take this further and implement the body of a Pair class (or trait), the type parameters A and B would be spread throughout your class, just as the symbol A was used in the LinkedList example.

Scala generic type parameter naming conventions

The same Oracle document lists the Java type parameter naming conventions. These are mostly the same in Scala, except that Java starts naming simple type parameters with the letter T, and then uses the characters U and V for subsequent types. The Scala standard is that simple types should be declared as A, the next with B, and so on. This is shown in Table 19-3.

Table 19-3. Standard symbols for generic type parameters

Symbol Description
A Refers to a simple type, such as List[A].
B, C, D Used for the 2nd, 3rd, 4th types, etc.
// from the Scala Styleguide
class List[A] {
  def map[B](f: A => B): List[B] = ...
}
K Typically refers to a key in a Java map. Scala collections use A in this situation.
N Refers to a numeric value.
V Typically refers to a value in a Java map. Scala collections use B in this situation.

See Also

  • Oracle’s Java “Generic Types” documentation
  • Recipe 19.4, “Make Mutable Collections Invariant”
  • Recipe 19.5, “Make Immutable Collections Covariant”
  • You can find a little more information on Scala’s generic type naming conventions at the Scala Style Guide’s Naming Conventions page

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:

You can find the Scala Cookbook at these locations:

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.