If you (like me) came from a Java background, you might be familiar with the covariance rules in Java.

Let’s try the following code snippet:

1
2
3
4
5
6
List<NonEmpty> nonEmptyThingies = new ArrayList<>();
nonEmptyThingies.add(new NonEmpty());
nonEmptyThingies.add(new NonEmpty());

// I'm sorry, Dave. I'm afraid you can't do that.
List<Thingy> thingies = nonEmptyThingies;

Where ‘Thingy’ is an interface which both ‘Empty’ and ‘NonEmpty’ implement.

If you try that, your code won’t compile:

incompatible types: java.util.List<blog.covariance.NonEmpty> cannot be converted
 to java.util.List<blog.covariance.Thingy>

You can get around this quibble using unbounded wildcards:

1
2
3
4
5
6
List<NonEmpty> nonEmptyThingies = new ArrayList<>();
nonEmptyThingies.add(new NonEmpty());
nonEmptyThingies.add(new NonEmpty());

// Alright, Dave. Since you said pretty please...
List<? extends Thingy> thingies = nonEmptyThingies;

Let’s try something naughty:

1
2
3
4
5
6
List<NonEmpty> nonEmptyThingies = new ArrayList<>();
nonEmptyThingies.add(new NonEmpty());
nonEmptyThingies.add(new NonEmpty());

List<? extends Thingy> thingies = nonEmptyThingies;
thingies.add(new Empty());

Once again, your code won’t compile:

incompatible types: java.util.List<blog.covariance.NonEmpty> cannot be converted
 to java.util.List<blog.covariance.Thingy>

Well, you’re trying to add an Empty thingy to a list of NonEmpty thingies, buddy…

The whole point of these code snippets is to show you that Java ‘List’ is invariant, that is, ‘List' is not a subtype of 'List' like 'NonEmpty' is a subtype of 'Thingy'.

Wanna play the same game with Java ‘Array’?

1
2
3
4
5
6
NonEmpty[] nonEmptyThingies = new NonEmpty[3];
nonEmptyThingies[0] = new NonEmpty();
nonEmptyThingies[1] = new NonEmpty();

// Whut? It compiles...
Thingy[] thingies = nonEmptyThingies;

Let’s try that naughty thing we tried to do with ‘List’:

1
2
3
4
5
6
7
NonEmpty[] nonEmptyThingies = new NonEmpty[3];
nonEmptyThingies[0] = new NonEmpty();
nonEmptyThingies[1] = new NonEmpty();

// Wut? Wut? Still compiles...
Thingy[] thingies = nonEmptyThingies;
thingies[2] = new Empty();

If you try to execute this snippet though:

java.lang.ArrayStoreException: blog.covariance.Empty

So Java ‘Array’ is covariant in the types of the objects they hold. ‘Array' is a subtype of 'Array'and if you try naughty things, like adding an Empty to a list of NonEmpty, you will get a nasty exception.

Java ‘Array’ being covariant is legacy design from when Java didn’t support generic types, which is a much safer and elegant way to accomplish the same things people were trying to achieve before Java 5, with covariance, when generic types weren’t available.

My initial plan was to get into covariance in Scala by starting from a familiar place, Java, but since this post is already quite long, I’ll leave it to next time. Covariance in Scala is quite a bit more fun, and tricky to get your head around it, since we can subtype and pass functions as arguments.