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

This text comes from the 2nd Edition of the Scala Cookbook.

Scala Problem

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

Solution

As a library writer, you’ll define generic types when declaring your classes. For instance, here’s a small linked-list class that’s written so that you can add new elements to it. It’s mutable in that way, like an ArrayBuffer:

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): Unit =
        val n = new Node(elem)
        n.next = head
        head = n

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

    def printAll() = printNodes(head)

Notice how the generic type A is sprinkled throughout the class definition. This generic type is a placeholder for actual types like Int and String, which the user of your class can specify.

For example, to create a list of integers with this class, first create an instance of it, declaring the type that it will contain to be the type Int:

val ints = 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 type String:

val strings = LinkedList[String]()
strings.add("Emily")
strings.add("Hannah")
strings.printAll()

Or any other type you want to use:

val doubles = LinkedList[Double]()
doubles.add(1.1)
doubles.add(2.2)

This demonstrates the basic use of a generic type when creating a class.

Discussion

When you use a “simple” generic parameter like A when defining a class, you can also define methods inside and outside of that class that use exactly that type. To explain what this means, start with this type hierarchy:

trait Person { def name: String }
class Employee(val name: String) extends Person
class StoreEmployee(name: String) extends Employee(name)

You might use this type hierarchy when modeling a point-of-sales application for a pizza store chain, where a StoreEmployee is someone who works at a store location. (You might then also have an OfficeEmployee type for people who work in the corporate office.)

Note: I omitted an image here that is shown in the Scala Cookbook.

Given this type hierarchy, you can create a method to print a LinkedList[Employee], like this:

def printEmps(es: LinkedList[Employee]) = es.printAll()

Now you can give printEmps a LinkedList[Employee], and it will work as desired:

// works
val emps = LinkedList[Employee]()
emps.add(Employee("Al"))
printEmps(emps)

So far, so good; this works as desired.

The limits of this approach

Where this “simple” approach doesn’t work is if you try to give printEmps a LinkedList[StoreEmployee]():

val storeEmps = LinkedList[StoreEmployee]()
storeEmps.add(StoreEmployee("Fred"))

// this line won’t compile
printEmps(storeEmps)

This is the error you get when you try to write that code:

printEmps(storeEmps)
          ^^^^^^^^^
          Found:    (storeEmps : LinkedList[StoreEmployee])
          Required: LinkedList[Employee]

The last line won’t compile because:

  • printEmps expects a LinkedList[Employee].

  • storeEmps is a LinkedList[StoreEmployee].

  • LinkedList elements are mutable.

  • If the compiler allowed this, printEmps could add plain old Employee elements to the StoreEmployee elements in storeEmps. This can’t be allowed.

As discussed in other solutions in the Scala Cookbook, the problem here is that when a generic parameter is declared as A in a class like LinkedList, that parameter is invariant, which means that the type is not allowed to vary when used in methods like printEmps. (A detailed solution to this problem is shown in the Scala Cookbook.)

Type parameter symbols

If a class requires more than one type parameter, use the symbols shown in Standard symbols for generic type parameters in Scala. For instance, in the official Java Generics documentation, Oracle shows an interface named Pair, which takes two types:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

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

trait Pair[K, V]:
    def getKey: K
    def getValue: V

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

>Note: I generally prefer using the symbols A and B for the first two generic type declarations in a class, >but in a case like this where the types clearly refer to key and value — such as in a Map class — >I prefer K and V. But use whatever makes sense to you.

The same Oracle document lists the Java type parameter naming conventions. These are similar 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 the first type should be declared as A, the next with B, and so on, as shown in Standard symbols for generic type parameters in Scala.

Table 1. Standard symbols for generic type parameters in Scala

Symbol Description

A

Refers to a simple type, such as List[A].

B, C, D

Used for the 2nd, 3rd, 4th types, etc. For example:

class List[A]:
    def map[B](f: A => B): List[B] = ???

K

Typically refers to a key in a Java map. (I also prefer K in this situation.)

N

Refers to a numeric value.

V

Typically refers to a value in a Java map. (I also prefer V in this situation.)

See Also