scalatype-boundsupperbound

Scala upper bounds


In a typical Scala upperbound example

abstract class Animal {
  def name: String
}

abstract class Pet extends Animal {}

class Cat extends Pet {
  override def name: String = "Cat"
}

class Dog extends Pet {
  override def name: String = "Dog"
}

class Lion extends Animal {
  override def name: String = "Lion"
}

What is the difference between this

class PetContainer[P <: Pet](p: P) {
      def pet: P = p
    }

val dogContainer = new PetContainer[Dog](new Dog)
val catContainer = new PetContainer[Cat](new Cat)

and this?

class PetContainer1(p: Pet) {
      def pet: Pet = p
    }

val dogContainer1 = new PetContainer1(new Dog)
val catContainer1 = new PetContainer1(new Cat)

What is the advantage of using an upper type bound vs using the abstract class/trait directly?


Solution

  • With upper bound you can have a collection of specific subtype - so limiting to only cats or dogs and you can get a specific subtype back from def pet. It's not true for PetContainer1.

    Losing more accurate type info example:

    val doggo: Dog = new Dog
    val dogContainer1 = new PetContainer1(doggo)
    // the following won't compile
    // val getDoggoBack: Dog = dogContainer1.pet  
    
    val dogContainer2 = new PetContainer[Dog](doggo)
    // that works
    val getDoggoBack: Dog = dogContainer2.pet  
    

    You can't put a cat into dog container:

    // this will not compile
    new PetContainer[Dog](new Cat)
    

    It would get more significant if you would be dealing with collections of multiple elements.