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. |
this post is sponsored by my books: | |||
![]() #1 New Release |
![]() FP Best Seller |
![]() Learn Scala 3 |
![]() Learn FP Fast |
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