Open In App

Scala – Contra-Variance

Last Updated : 03 Jul, 2020
Improve
Improve
Like Article
Like
Save
Share
Report

Contra-Variance is exactly the opposite of covariance. Contra-Variance creates a similar subtyping relation between class and type parameter. So, contra-variance states that if there are two parameterized types such that S is a subtype of T, then List[T] is a subtype of List[S]. We can make a generic class contra-variant by using notation [-S]. We can use contra-variance whenever the type (generic) parameter “consumes” type S or types that accept. We can also use contra-variant as a method argument type, but not as a method return type because it will generate a compilation error.

Syntax:

List[-T]

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

Example 1: Let’s consider an example where a Printer[S] is a class that prints some type S. We will define here subclasses for a type:




// Scala program to illustrate contra-variance 
  
abstract class Printer[-S] 
{  
    // Here -S denotes Contra-Variant 
    // type S
    def print(value: S): Unit
  
abstract class Flower
{
    def name: String
}
  
// Create subclass of Flower
case class Lily(name: String) extends Flower
  
class FlowerPrinter extends Printer[Flower] 
{
    def print(flower: Flower): Unit =
      
    // Display name of flower
    println("The flower's name is: " + flower.name)
}
  
class LilyPrinter extends Printer[Lily] 
{
    def print(lily: Lily): Unit =
    println("Lily's name is: " + lily.name)
}
  
// Inherit the main method
object Contravariance extends App
{
      
    // Assign value    
    val lily : Lily = Lily("White Lily"
  
    // Define method to print the name
    def printMyLily(printer: Printer[Lily]): Unit =
    {
        printer.print(lily)
    }
  
    // Create objects
    val lilyPrinter: Printer[Lily] = new LilyPrinter
    val flowerPrinter: Printer[Flower] = new FlowerPrinter
  
    printMyLily(lilyPrinter)
    printMyLily(flowerPrinter)
}


Output:

Lily's name is: White Lily
The flower's name is: White Lily

Explanation: Here in this code, if a Printer[Lily] can print any Lily and a Printer[Flower] can print a Flower, then a Printer[Flower] can also print Lily. But the inverse can’t be done as the Printer[Lily] doesn’t know how to print any Flower. So, we can use Print[Flower] as an alternative for Printer[Lily] and we can do so by making Printer[S] Contra-Variant.

Example 2: Consider this example generic class vehicle:




// Scala program to illustrate contra-variance 
  
abstract class GetVehicleName[-S] 
{  
    // Here -S denotes Contra-Variant
    // type S
    def print(value: S): Unit
}
  
abstract class Vehicle
{
    def name: String
}
  
// Create subclass of Flower
case class Bike(name: String) extends Vehicle
  
class VehicleName extends GetVehicleName[Vehicle]
{
    def print(vehicle: Vehicle): Unit =
    println("The vehicle's name is: " + vehicle.name)
}
  
class BikeName extends GetVehicleName[Bike] 
{
    def print(bike: Bike): Unit =
    println("Bike's name is: " + bike.name)
}
  
// Inherit the main method
object Contravariance extends App
{
      
    // Assign value    
    val bike : Bike = Bike("Yamaha Fazer"
  
    // Define method to print the name
    def printMyBike(getVehicleName: GetVehicleName[Bike]): Unit =
    {
        getVehicleName.print(bike)
    }
  
    // Create objects
    val bikeName: GetVehicleName[Bike] = new BikeName
    val vehicleName: GetVehicleName[Vehicle] = new VehicleName
  
    printMyBike(bikeName)
    printMyBike(vehicleName)
}


Output :

Bike's name is: Yamaha Fazer
The vehicle's name is: Yamaha Fazer

Explanation: In this example, we get the same output for both due to contra-variance. As abstract class Get_vehicle_name is contra-variant, we can replace Get_vehicle_name[Vehicle] for Get_vehicle_name[Bike], but inverse is not allowed.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads