Scala Variances, Upper type Bounds and Lower type bounds With Examples

Variance refers as how subtyping between complex types relates to subtypes of their components. Scala supports variances annotations of type parameters of a generic class.

The type of variance annotations supported in Scala are;

Types Definition Scala Notation
Covariant C[T’] is a subclass of C[T] [+T]
Contravariant C[T] is a subclass of C[T’] [-T]
Invariant C[T] and C[T’] are not related [T]

class Array[+X] {
  def add[Y >: X](elem: Y): Array[Y] = new Array[Y] {
    override def first: Y = elem
    override def retrieve: Array[Y] = Array.this
    override def toString() = elem.toString() + "n" + Array.this.toString()
  def first: X = sys.error("No elements in the Array")
  def retrieve: Array[X] = sys.error("Array is empty")
  override def toString() = ""
object ArrayTest extends App {
  var a: Array[Any] = new Array().add("US");
  //a = a.add(new Object())
  a = a.add(56)
  a = a.add(67.89)
  println("Array elements added are: " + a)

The annotation +X indicates that X can be used in covariant positions only. The annotation -X indicates that X can be used in contravariant positions only. Array[X] is a subtype of S if X is a subtype of S.

In this example we are defining a covariant type parameter X to define the method add. We have a polymorphic method in which we use the element type X as a lower bound of add method type variable thereby bringing the variance of X in sync with its declaration as covariant parameter. We are inserting elements of String,Integer and float types.

Upper type Bounds

An upper type bound X <: B declares that type variable X refers to a subtype of type B.

Consider an example below.

trait Employee {
  def EmployeeIdexists(x: Any): Boolean
case class EId(a: Int) extends Employee {
  def EmployeeIdexists(x: Any): Boolean =
    x.isInstanceOf[EId] &&
      x.asInstanceOf[EId].a == a
object TestEmployee extends App {
  def findEId[A <: Employee](d: A, ls: List[A]): Boolean =
    if (ls.isEmpty) false
    else if (d.EmployeeIdexists(ls.head)) true
    else findEId[A](d, ls.tail)
  val elist: List[EId] = List(EId(25), EId(26), EId(27), EId(28))
  if ((findEId[EId](EId(28), elist)))
    println("Employee Id exists")
  else println("Employee Id does not exist")

This example find whether the employee id exists or not. We are defining a method findEId which creates an integer list and checks whether the id specified is contained in the list.


Lower type Bounds

The term X >: B expresses that the type parameter X or the abstract type X refer to a supertype of type B. Lower type bounds declare a type to be a supertype of another type.

Create the scala class ListNode as;

case class ListNode[+T](h: T, t: ListNode[T]) {
  def head: T = h
  def tail: ListNode[T] = t
  def prepend[U >: T](elem: U): ListNode[U] =
    ListNode(elem, this)

We are creating a class ListNode and defining the prepend method where T only appears in covariant positions. U >: T specifies the abstract type U is a supertype of T.

The type variable T appears as a parameter type of method prepend, this rule is broken. With the help of a lower type bound, though, we can implement a prepend method.

Now create a scala object as;

object LowerBoundTest extends App {
  val empty: ListNode[Null] = ListNode(null, null)
  val strList: ListNode[String] = empty.prepend("hello")
  val anylist: ListNode[Any] = strList.prepend(12345)


That’s all for now, we will look into more scala features in future posts.

By admin

Leave a Reply

%d bloggers like this: