Scala – Contra-Variance
Last Updated :
03 Jul, 2020
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:
abstract class Printer[-S]
{
def print(value : S) : Unit
}
abstract class Flower
{
def name : String
}
case class Lily(name : String) extends Flower
class FlowerPrinter extends Printer[Flower]
{
def print(flower : Flower) : Unit =
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)
}
object Contravariance extends App
{
val lily : Lily = Lily( "White Lily" )
def printMyLily(printer : Printer[Lily]) : Unit =
{
printer.print(lily)
}
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:
abstract class GetVehicleName[-S]
{
def print(value : S) : Unit
}
abstract class Vehicle
{
def name : String
}
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)
}
object Contravariance extends App
{
val bike : Bike = Bike( "Yamaha Fazer" )
def printMyBike(getVehicleName : GetVehicleName[Bike]) : Unit =
{
getVehicleName.print(bike)
}
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.
Share your thoughts in the comments
Please Login to comment...