Open In App

Scala | Variances

Last Updated : 10 Apr, 2019
Improve
Improve
Like Article
Like
Save
Share
Report

Variance is the interconnection of Sub-Typing relationships which are either of complicated types or of their constituent types. Variance explains inheritance correlation of Types that have parameters or arguments within them. These types belongs to the generic classes, which takes a type like a parameter. In the presence of Variance one can create relations between complicated types and in its absence we won’t be able to reiterate the abstraction class. The Scala Variances are of three types, which are as follows:

  1. Covariant
  2. Contravariant
  3. Invariant

Some important points:

  • In Scala collection’s types can be constructed more securely through Variances.
  • Variances can provide us with some extra adjustable advancements.
  • It also helps in the development of authentic applications.
  • Variances can be applied on any Scala Types like List, Sets, etc.
Types of Variances

Let’s discuss each type in detail.

  • Covariant: If a generic class has a type parameter T, then its Covariant notation will be [+T]. Suppose, we have two List types of Scala i.e, S and T. where, S is sub-type of T, then you can say that List[S] is also the sub-type of List[T]. If two types are related like this then they fall under the Covariant type. List[T] can be called as Generic.
    Syntax:

    List[+T]

    Here, T is the type parameter and + is the symbol of Covariance.
    Example:




    // Scala program of covariant
    // type
      
    // Creating an abstract class
    // for Student
    abstract class Student
    {
        def name: String
    }
      
    // Creating a sub-class Girls
    // of Student
    case class Girls(name: String) extends Student
      
    // Creating a sub-class Boys
    // of Student
    case class Boys(name: String) extends Student
      
    // Creating an Object Covariance
    // that inherits main method of
    // App
    object Covariance extends App
    {
        // Creating a method
        def Studentnames(students: List[Student]): Unit =
        {
            students.foreach { student =>
          
            // Displays students name
            println(student.name)
        }
        }
      
        // Assigning names 
        val boys: List[Boys] = List(Boys("Kanchan"), Boys("Rahul"))
        val girls: List[Girls] = List(Girls("Nidhi"), Girls("Geeta"))
      
        // Accessing list of boys
        Studentnames(boys)
      
        // Accessing list of girls
        Studentnames(girls)
    }

    
    

    Output:

    Kanchan
    Rahul
    Nidhi
    Geeta
    

    Here, List of boys and girls both belongs to the List of students as they are its sub-type and so, here names of all the students are displayed when the Super-type Student is called.
    Note:

    • Abstract class is utilized here to apply covariance as it has List[+T] with it where, the type parameter T is covariant.
    • A trait App is used here to speedily change objects into workable programs.
  •  

  • Contravariant: If a generic class has a type parameter T, then its Contravariant notation will be [-T]. Suppose, we have two List types of Scala i.e, S and T. where, S is sub-type of T, but List[T] is the sub-type of List[S]. If two types are related like this then they fall under the Contravariant type. It is opposite of covariant.
    Syntax:

    List[-T]

    Here, T is the type parameter and is the symbol of Contravariance.
    Example:




    // Scala program of Variance of
    // Contravariant type
      
    // abstract class with a contravariant 
    // type parameter
    abstract class Show[-T] 
    {
      
        // Method for printing
        // type T
        def print(value: T): Unit
    }
      
    // A class structure
    abstract class Vehicle
    {
        def name: String
    }
      
    // Creating sub-class of Vehicle
    case class Car(name: String) extends Vehicle
      
    // Creating sub-class of class
    // Show
    class VehicleShow extends Show[Vehicle] 
    {
        def print(vehicle: Vehicle): Unit =
      
        // Displays name of the vehicle
        println("The name of the vehicle is: " + vehicle.name)
    }
      
    // Creating sub-class of class
    // Show
    class CarShow extends Show[Car] 
    {
        def print(car: Car): Unit =
      
        // Displays name of the car
        println("The name of the car is: " + car.name)
    }
      
    // Inheriting main method of 
    // the trait App
    object Contravariance extends App 
    {
      
        // Assigning value to the name 
        val newCar: Car = Car("Scorpio")
      
        // Defining a method that
        // prints the name
        def printnewCar(show: Show[Car]): Unit = 
        {
            show.print(newCar)
        }
      
        // Creating objects
        val showcar: Show[Car] = new CarShow
        val showvehicle: Show[Vehicle] = new VehicleShow
      
        // Accessing name
        printnewCar(showcar)
        printnewCar(showvehicle)
    }

    
    

    Output:

    The name of the car is: Scorpio
    The name of the vehicle is: Scorpio
    

    It is Contravariant so, we are able to substitute Show[Vehicle] for Show[Car] and that’s why both vehicle and car returns the same name.

  • Invariant: In Scala, generic types are by default invariant. Suppose, we have two List types of Scala i.e, S and T. where, S is sub-type of T but List[T] and List[S] are not at all related, then they fall under the invariant type.
    Syntax:

    List[T]

    Here, we don’t use any symbol for invariant relationship.
    Note: The classes like Array[T], ListBuffer[T], ArrayBuffer[T] etc. are mutable so, they have invariant type parameter, if we use invariant type parameters in inheritance relationship or sub-typing then we will get a compilation error.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads