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:
- Covariant
- Contravariant
- Invariant
- 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 // 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: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:Kanchan Rahul Nidhi Geeta
- 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 // 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: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.The name of the car is: Scorpio The name of the vehicle is: Scorpio
- 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.