Now, do you remember the name of this article. I know this is a long one but it’s time to begin our journey to the interesting part — Variance. Let’s consider this class hierarchy below for all the discussion ahead. All examples will be in Java.

// Java public class Animal {

String name;

int troubleMakerRating;



Animal(String name) {

this.name = name;

this.troubleMakerRating = new Random().nextInt();

}

}



public class Dog extends Animal {

Dog(String name) {

super(name);

}

}



public class Cat extends Animal {

Cat(String name) {

super(name);

}

}

There is a class Animal . Dog & Cat extend Animal , because a dog is an animal, a cat is an animal. Each Animal has a name and a troubleMakerRating . So according to the Substitution Principle (SP), If we have a collection of Animals, it’s permissible to add a Dog or a Cat instance to it:

List<Animal> animals = new ArrayList<Animal>();

animals.add(new Animal("Some Lion"));

animals.add(new Dog("German Shepherd"));

animals.add(new Cat("RagDoll"));

Here subtyping is in two forms:

1. Adding to list is permitted because Dog/Cat is a subtype of Animal .

2. Assignment operation is permitted because ArrayList is a subtype of List .

So, if Dog -> Animal , ArrayList -> List it seems very reasonable that an ArrayList<Dog> -> List<Animal> . Let’s write this down in code:

List<Animal> animals = new ArrayList<Dog>();

Try compiling above piece of code. It won’t. Compiler throws Incompatible Types . Compiler does not let us substitute List<Animal> with ArrayList<Dog> because its not safe.

List<Animal> animals = new ArrayList<Dog>(); // Compilation ERROR !!

animals.add(new Cat("Birman"));

If there was no compilation error at the first line, then under Substitution Principle, it would be permissible to add an instance of Cat to the list of animals which is actually a list of Dog and Cats and Dogs don’t go along well. Issues would be seen at run time when we are parsing through the list of Animals thinking they are all Dogs but a Cat pops out. Remember the rule: Compile Time Error is Good, Run time Error is bad. That’s why, Substitution Principle does not apply here. i.e. ArrayList<Dog> !-> List<Animal> .

But there are also cases where we want Substitution Principle to work, i.e. We want the Compiler to relax a bit as we know what we are doing.

void printNames(Collection<Animal> animals) {

animals.forEach(animal -> System.out.println(animal.name));

} List<Dog> myDogs = new ArrayList<>();

myDogs.add(new Dog("German Shepherd"));

myDogs.add(new Dog("Bulldog"));

myDogs.add(new Dog("Pug"));



printNames(myDogs); // Compilation ERROR !! Incompatible Types

A function printNames takes in a list of animals and prints their name. So, we should be able reuse the function to print the names of a dog list. After all the function printNames is only accessing the animals from the list and not trying to write into it. But compiler’s job is to be safe, so it errors out on printNames(myDogs) . Over smart Compiler!!

And, what if we need to compare two dogs using a generic Animal Comparator:

// 1

void printNameOfMostTroubleMakerDog(List<Dog> dogs,

Comparator<Dog> comparator) {



Dog maxTroubleMaker = dogs.get(0); for (int i = 1; i < dogs.size(); i++) {

if (comparator.compare(maxTroubleMaker, dogs.get(i)) > 0) {

maxTroubleMaker = dogs.get(i);

}

} System.out.println(maxTroubleMaker.name

+ " is the most trouble making Dog.");

} // 2

Comparator<Animal> troubleMakerComparator = Comparator.comparingInt(animal -> animal.troubleMakerRating); // 3 - Compilation ERROR !! Incompatible Types

printNameOfMostTroubleMakerDog(myDogs, troubleMakerComparator)

printNameOfMostTroubleMakerDog takes a list of dogs and prints the name of most trouble making dog based on an input comparator. We create a reusable troubleMakerComparator which compares trouble maker rating of each animal. It does not compile, because printNameOfMostTroubleMakerDog is expecting a Comparator<Dog> but we are passing in a Comparator<Animal> .

Compiler should have allowed this. If a Comparator<Animal> can compare two Animals, it definitely can compare two Dogs.

Now the question arises, if we know that some of the substitutions like above are safe, shouldn’t we be able to convey this to compiler too? If the answer to every question was No, there would be no need for this article. 😛

Remember WildCards ( ? ). Yes we are going to use them now.

Variance

Variance refers to how subtyping between more complex types relates to subtyping between their components. For example, how should a list of Cats relate to a list of Animals , Or how should a function returning a Cat relate to a function returning a Animal .

There are four possible variances:

Covariance : Preserves ordering of subtyping relation. i.e.

if A -> B , then Box<A> -> Box<B>

: Preserves ordering of subtyping relation. i.e. if , then Contravariance : Reverses the ordering of subtyping relation. i.e.

if A -> B , then Box<B> -> Box<A>

: Reverses the ordering of subtyping relation. i.e. if , then Bivariance : If both of the above applies. i.e.

if A -> B , then Box<A> -> Box<B> & Box<B> -> Box<A>

: If both of the above applies. i.e. if , then & Invariance: Neither of Covariance or Contravariance apply.

To solve the substitution problem we faced when using printNames & printNameOfMostTroubleMakerDog , we will make use of Covariance & Contravariance respectively.

Covariance

If somehow, ArrayList<Dog> becomes a subtype of List<Animal> , printNames function becomes reusable for any list which contains types that extend Animal. This is how we do it:

void printNames(Collection<? extends Animal> animals) {

...

}



printNames(myDogs); // Compiles cleanly

Now printNames(myDogs); type-checks and compiles. Notice the change in signature. printNames does not accept a Collection<Animal> anymore but a Collection<? extends Animal> . When we use ? extends , we are basically telling compiler that a collection of items whose Type is upper bound to Animal is accepted by the function printNames .

This does two things:

It makes List<Dog> -> Collection<Animal> . This is called Covariance. And, inside printNames we can NOT add any item to the collection, but we can parse or access the items out of the list. We can not add because it’s possible that this list might be a list of dogs (a homogeneous list) or list of some animals (a heterogeneous list). Since we are not sure what the underlying list is, compiler does not allow any addition to the collection.

We can also use wildcards when declaring variables. Ex:

1| List<Dog> dogs = new ArrayList<Dog>();

2| dogs.add(new Dog("PitBull"));

3| dogs.add(new Dog("Boxer"));

4| List<? extends Animal> animals = dogs;

5| animals.add(new Cat("Sphynx Cat")); // Compilation ERROR !!

Without ? extends , 4th line would have caused compilation error because List<Dog> !-> List<Animal> , but 5th would have worked because Cat -> Animal . With ? extends 4th line compiles because now List<Dog> -> List<Animal> but 5th does not compile because you cannot add a Cat to a List<? extends Animal> , since it might be a list of some other subtype of Animal.

Another way to understand is When we extend , there is atleast an upper bound type, so we know what type we are getting out of the Box. So GET is allowed, because that's the safest option for the compiler.

General Rule of Thumb:

If a structure contains elements with a type of the form ? extends E , we can GET elements out of the structure, but we CAN NOT PUT elements into it.

Contravariance

Similarly, to solve for the comparator problem, we change the function printNameOfMostTroubleMakerDog to include a wildcard and make it ? super Dog . i.e:

void printNameOfMostTroubleMakerDog(List<Dog> dogs,

Comparator<? super Dog> comparator) {

...

}

... // Compiles Cleanly

printNameOfMostTroubleMakerDog(myDogs, troubleMakerComparator);

Now printNameOfMostTroubleMakerDog(myDogs, troubleMakerComparator) type-checks. ? super Dog means any type whose lower bound is Dog i.e. any type which is a supertype of Dog. So Animal & Object are acceptable types here. This also makes Comparator<Animal> a subtype of Comparator<Dog> for this method. This is Contravariance. There must be some corollary here from the GET-PUT principle we saw above and in fact there is. We can now PUT items inside a such a structure but we can not GET items out if it. We will need a different example for it.

public static void add_N_DogsTo(List<? super Dog> someList,

int count) { if (count == 0)

return;



for (int i = 0; i < count; i++)

// Adding to the list is allowed

someList.add(new Dog("Stray Dog " + i )); // Reading from the list is NOT allowed. Compilation ERROR !!

Animal d = someList.get(0);

}

If we have a function to add ’N’ dogs to some list, whose lower bound is a Dog , adding is allowed but reading from list isn’t. This is because, during PUT we have a concrete type we can add, but during a GET command, we can never know the type of output object. It could be an Animal or an Object .

Note: Object d = someList.get(0); works because in Java, everything extends Object. So there is always an upper bound.

Another way to understand is When we super , there is atleast a lower bound type, so we know what's smallest type we can put in the box. So PUT is allowed, because that's the safest option for the compiler.