Contra-Varaince
For most of the Scala developers, contra-variance is most of the trickiest concepts in the Scala type system, but as per my experience if you have a clear picture of co-variance like how it works, why compiler allows the parameter position, why not and most important things are our varaince secrets than contra-variance is simple and easy to understand. Before proceeding to contra-variance, if you have any doubt within co-variance please go once again for clear the picture.
In contra-variance, the inheritance hierarchy arrows are going to be flip. As you can see in our Diagram 10.1, where Garage[Car]
is actually a subtype of Garage[Lamborghini]
, but always remember those flips are only for parameterized types object like Garage[Car]
(Garage of Car) not with actual type hierarchy like Car
, Lamborghini
and more. For creating a contra-variance type there is a special syntax like co-variance as below:
For creating a contra-variance type, we need to use (-) minus/hyphen symbol with type parameter similar to co-variance and all position restrictions are applied but different from co-variance. Let’s create an example of contra-variance:
As in the example, some mysterious things are happend from co-varaince experience. While we passing supertype of TrainingSchool[Vehicle]
is TrainingSchool[Car]
to driveTheCar(TrainingSchool[Car], Car)
method, the code compiles and run successfully but for passing subtype TrainingSchool[Lamborghini]
, we are getting compile time error.
This what we discussed in contra-variance earlier definition, where we mentioned inheritance arrows are actually flipped, now TrainingSchool[Vehicle]
type object is a subclass of TrainingSchool[Car]
type object but not actually hierarchy effects like Vehicle
is a still subclass of Car
. This is the reason, we can pass TrainingSchool[Vehicle]
type object to driveTheCar(TrainingSchool[Car], Car)
method but not TrainingSchool[Lamborghini]
type object. There are rules, which we will explore step by step.
Like co-variance, in contra-variance, we have important catch or you can say rule
While creating a generic type with the contra-variance flag (-), the positions of the type parameter are only valid at methods arguments location as below:
Contra-Variance FAQ
Why it allows to using type parameter T at method argument location not return type location?
Let's explore the answer via code as below:
In case of passing vehicleSchool
type object, at runtime washTheVehicle
method from TrainingSchool[Vehicle]
type is called, which returns Vehicle
type object which is technical invalid to catch supertype object to subtype according to polymorphism, that’s why compiler gives us an error for using type parameter at return type position in conta-varaince.
Why it doesn’t allow us to pass subtype?
If it allows to passing subtype than we are generating the same scenario, which we discussed in co-variance where it doesn’t allow to declaring the type parameter at method return type position, not in method arguments position. Let's take another example:
Method call driveTheCar(lamborginiSchool, new Jaguar)
could generate runtime exceptions because TrainingSchool[Car] driveTheVehicle
method could be using Lamborghini
driving features which are not available in Jaguar
.
How it works for passing the supertype object?
Let's jump to the code:
The reason driveTheCar(vehicleSchool, new Car)
method call works, because compiler knows during runtime, TrainingSchool[Vehicle] driveTheVehicle
method call, which is using the features of Vehicle
and compiler knows, Car
is a subclass of Vehicle
, that means all of the Vehicles
features are also available in Car
, so, there could be no problem if someone passing supertype object. This is the reason, compiles allows to passing supertype in contra-variance.
Last updated