Generic
Generic class
A class (or trait) can have multiple generic types.
object
can't have generic types.
class MyNode[T]
Generic method
object MyList {
def first[T]: MyNode[T] = ???
}
val firstNode = MyList.first[Int]
Variance Problem
class Animal
class Cat extends Animal
class Dog extends Animal
Covariance
class CovariantList[+T]
val catList: CovariantList[Animal] = new CovariantList[Cat]
The generic type in the class' declaration can be the super type of the class' instance.
Invariance
class InvariantList[T]
val animalList: InvariantList[Animal] = new InvariantList[Animal]
The generic type in the class' declaration must be exact type of the class' instance.
Contravariance
class ContravariantList[-T]
val trainerList: ContravariantList[Cat] = new ContravariantList[Animal]
The generic type in the class' declaration can be the sub type of the class' instance.
The trainers of Animal are those of Cat, too.
Bounded Type
There are 2 types of bounds:
-
Super bound
<:
class Cage[T <: Animal](animal: T) val cage = new Cage(new Animal)
It means the type
T
should beAnimal
or its sub-types, such asCat
andDog
. -
Lower bound
>:
class CatTraining[T >: Cat](animal: T) val catTraining = new CatTraining(new Animal)
It means the type
T
should beCat
or its super-types, likeAnimal
.
Bounded Type help solve the variance problem.
The problem is if we have a class class MyList[+T]
, then:
val myList: MyList[Animal] = new MyList[Cat]
myList.add(new Dog) // Does it work?
The answer is yes. Because myList
is a list of Animal
even if we initialize it as a list of Cat
.
To implment add
method, we need to use bounded type.
class MyList[+T] {
// def add[T](element: T): MyList[T] = ???
// Error. Because Nothing is sub-type of all the types.
// The solution is as followed.
def add[SupT >: T](element: SupT): MyList[SupT] = ???
}