Scala - Type Bounds



Type Bound in Scala is like a rule. Type Bound limits what a parameter and variable can be. You can set the limits for the variables in the Scala. You can also put code into real world examples. In Scala, type bounds have the same purpose by imposing some restrictions on factors.

Types of Type Bounds

There are two types of type bounds. These are Upper type bound and lower type bound. We will discuss these both upper and lower type bounds here.

1. Lower Type Bound

Lower type bound is used to declare a higher type for another using lower type bounds. You can use >: for a lower bound in Scala. You can write a lower bound as `[T >: S]` in Scala, where S is a type and T is a type parameter. The statement says T must be S or a bigger type than S.

What is Supertype here ? Supertypes are higher types for another using lower type bounds. For example, S >:T this lower bound has T is less than or equal to S. It means T is bound in this case. Therefore, T is a supertype of S.

Syntax

[T >: Demo[T]]

We have a lower defined lower bound T which must be either Demor or Supertype of Demo type.

For example, if we declare all classes in a specific format, like this:

trait Animal
class Mammal extends Animal
class Bird extends Animal
class Dog extends Mammal
class Cat extends Mammal
class Parrot extends Bird
class Penguin extends Bird

class Habitat[A](val location: A)

All classes and subclasses are now declared with the above statements. In the statement Habitat[A], 'Habitat' is general, and 'A' is the abstract type. You can put, Habitat[A] means the value for 'location' must be of type A. If you use the command Habitat[fruits], it creates an instance of the 'Fruits' type.

For example,

new Habitat[fruits](new fruits)
new Habitat[fruits](new drinks)
new Habitat[eatables](new vegetables)

In the given example, the first statement compiles. But the second shows errors because 'fruits' and 'drinks' are different types. However, the third statement compiles because 'vegetable' is a subtype of 'eatables.'

For example, using lower bound in the above statements:

class Habitat[A >: fruits](val location: A)

So, in this statement, you position any supertype of 'fruits' like 'eatables' or 'Habitat'. Therefore, you can see that Scala lower bounds are useful and adaptable in real-world examples with logical reasoning. You can impose real-life limitations on variables used in the coding language.

Example

// Scala Program To Demonstrate Scala Lower Bound 
class Tutorialspoint 
class Author extends Tutorialspoint 
class Tutorial extends Tutorialspoint 

class ComputerSciencePortal { 
   // Declaration of Lower bound 
   def display[T >: Tutorial](d: T) { 
      println(d) 
   } 
} 

// Object created 
object ScalaUpperBounds { 
   // Driver code 
   def main(args: Array[String]) { 

      val tutorialspoint = new Tutorialspoint 
      val author = new Author 
      val tutorial = new Tutorial 

      val computerscienceportal = new ComputerSciencePortal 

      computerscienceportal.display(tutorialspoint) 
      computerscienceportal.display(tutorial) 
      computerscienceportal.display(author) 
   } 
}

Output of this code will be:

Tutorialspoint@<hashCode>    // Output of computerscienceportal.display(tutorialspoint)
Tutorialspoint@<hashCode>    // Output of computerscienceportal.display(tutorial)
Author@<hashCode>           // Output of computerscienceportal.display(author)

In the class 'computerscienceportal' a lower bound is set. 'tutorialspoint' is the superclass of 'Author,' and it's accepted in the lower bound.

Example

// Scala Program To Demonstrate Scala Lower Bound 
class Animal
class Mammal extends Animal
class Dog extends Mammal
class Cat extends Mammal

class Zoo {
   // Declaration of lower bound 
   def display[T >: Mammal](m: T) {
      println(m)
   }
}

// Object created 
object ScalaUpperBounds {
   // Driver code 
   def main(args: Array[String]) {
      // Defined new variable 
      val animal = new Animal
      val mammal = new Mammal
      val dog = new Dog
      val cat = new Cat

      val zoo = new Zoo

      zoo.display(animal)
      zoo.display(mammal)
      zoo.display(dog)
      zoo.display(cat)
   }
}

Output of above code will be,

Animal@<hashCode>    // Output of zoo.display(animal)
Mammal@<hashCode>    // Output of zoo.display(mammal)
Dog@<hashCode>       // Output of zoo.display(dog)
Cat@<hashCode>       // Output of zoo.display(cat)

2. Upper Type Bound

Scala restricts Type Parameters and Type Variables with type bounds, and Upper bound is one of them. Implementing Type Bounds clarifies Type Variable constraints. It limits their specific values and details about their types of members. We use an upper bound on type parameters.

Syntax

[T <: S]

In '[T <: S],' 'T' is a type parameter, and 'S' is a type. It means T must be either S or a subtype of S.

For example, upper bound,

// Scala Program To Demonstrate Scala Upper Bound 
class Tutorialspoint 
class Author extends Tutorialspoint 
class Tutorial extends Author 

class OnlineLearningPortal {
   // Declaration of upper bound 
   def display[T <: Author](d: T) {
      println(d)
   }
}

// Object created 
object ScalaUpperBounds {
   // Driver code 
   def main(args: Array[String]) {
      val tutorialspoint = new Tutorialspoint
      val author = new Author
      val tutorial = new Tutorial

      val onlineLearningPortal = new OnlineLearningPortal

      // onlineLearningPortal.display(tutorialspoint)  // This line is commented out because tutorialspoint does not satisfy the upper bound constraint.
      onlineLearningPortal.display(tutorial)
      onlineLearningPortal.display(author)
   }
}

Output of this code will be,

Tutorial@<hashCode>    // Output of onlineLearningPortal.display(tutorial)
Author@<hashCode>      // Output of onlineLearningPortal.display(author)

In the 'onlineLearningPortal' class, an upper bound is set. Since 'Tutorialspoint' is the superclass of 'Author', it is not accepted.

Example

// Scala Program To Demonstrate Scala Upper Bound 
class Employee
class Manager extends Employee
class TeamLead extends Manager

class Company {
   // Declaration of upper bound 
   def display[T <: Manager](t: T) {
      println(t)
   }
}

// Object created 
object ScalaUpperBounds {
   // Driver code 
   def main(args: Array[String]) {
      // Defined new variable 
      val employee = new Employee
      val manager = new Manager
      val teamLead = new TeamLead

      val company = new Company

      // company.display(employee)  // This line is commented out because the employee does not satisfy the upper bound constraint.
      company.display(manager)
      company.display(teamLead)
   }
}

Output of this code will be,

Manager@<hashCode>    // Output of company.display(manager)
TeamLead@<hashCode>   // Output of company.display(teamLead)

In the 'Company' class, we have set an upper bound as [T <: Manager]. This means the 'display' method accepts objects of the 'Manager' class or its subclasses (like 'TeamLead'). Superclasses of 'Manager' will not be accepted. If the superclass of 'Manager,' i.e., 'Employee,' is not commented, a type mismatch error will occur.

Advertisements