Home Scala Generic
Post
Cancel

Scala Generic

Scala Generic

Scala type system support generic. Generic classes are classes that take a type(s) as parameter. For example, List[T], Seq[T], Option[T].

The following code will be used as an example for the rest of this article.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
trait Animal

abstract class Mammal extends Animal {
  def name(): String
}
case class Dog(name: String) extends Mammal
case class Cat(name: String) extends Mammal

abstract class Fish
case class Salmon() extends Fish
case class Mackerel() extends Fish

//           Animal
//       |            |
//    Mammal         Fish
//   |       |     |      |
//  Dog     Cat  Salmon  Mackerel

Upper bound and lower bound

Upper bound / Lower bound is the way to restrict that possible type that can be received.

Systax

1
2
3
[T <: UpperBoundType] 
[T >: LowerBoundType]
[T <: UpperBoundType >: LowerBoundType]
  • Upper bound : The way to limited type parameter. Let say, we want to write a method that take wrapper of mammal ```scala val dog = Dog(“Tu”) val cat = Catog(“Pom”)

// the below method will cause complie error because it doesn’t understand name() method that it exists. Therefore, we need to tell complier that T in this method will only Mammal or class below it. def mammalSpeakT: Unit = { println(mammal.name()) }

// to fix it, we need to add Upperbound => <: Mammal def mammalSpeakT <: Mammal: Unit = { println(mammal.name()) }

1
2
3
4
5
6
- Lower bound : Normally, we should not use lowerbound, except we develop library. Anyway, the logic is similar to upperbound but opposite instead. The type of lower bound is restrict that only accept the classes that are ancestor of the bounded type.

```scala
def classThatAreSuperOfDog[T >: Dog](wrapper: Wrapper[T]): Unit = {
  println(wrapper.toString)
}

Covariant

Covariant is the concept that describe the relation of Generic classes regarding the type parameter. The reason that this exists is because that complier cannot assume the relation of Wrapper classes(List, Seq, Option, and so on). List[Dog] is not consider to be subtype of List[Animal] unless we specify that it is.

There are 3 type of relationship

  • Covariance : This means the relation of Wrapper class is aligned the same way of its generic type
  • Contravariance : This means the relation of Wrapper class is aligned the opposite way of its generic type
  • Invariance : This is the default one which means there isn’t relation at all.

Usecase 1

1
2
3
4
5
6
7
8
9
10
case class Wrapper[T](data: T) {
  def underlining(): T = data
}

val w1: Wrapper[Dog] = Wrapper(Dog("asd"))
val w2: Wrapper[Animal] = w1 //this cause complie error because Wrapper[Dog] isn't subtype of Wrapper[Animal]. We have to tell complier explicitly by add `Covariance` `+` sign to generic type

case class Wrapper[+T](data: T) {
  def underlining(): T = data
}

//TODO Usecase 2

1
This post is licensed under CC BY 4.0 by the author.