Tag Archives: Generics

A Pretty Specific Generics Problem

So today I’m like coding and stuff (but you already knew that) when I ran into this conundrum with generics. Can you see why this doesn’t compile? Assume Car is the superclass of Ford and Toyota.

List<? extends Car> list = new ArrayList<Car>();
list.add(new Ford());
...
Compile error: cannot find symbol method add(Ford)

What??? Regroup. I have a List of anything that extends Car and I can’t put a Ford in it?  To see why this is actually correct, let’s go back to the basics. Consider:

Car myCar = new Ford();

Here we have a Car reference to a specific Car implementation, namely Ford. We can use this Ford object with the Car reference according to Car’s exposed interface. However, we can’t use that reference to access Ford-specific functionality. Now take another look at the list reference:

List<? extends Car> list = ...;

What this means is that list is a reference to a List of something that extends Car (or is Car), but we don’t really know what that something is. In other words, it could be a reference to a List<Toyota>, List<Ford>, or List<Car>. The only functionality available through the List<? extends Car> reference is the lowest common denominator for all such List objects – basically little more than iterating. Hence when we tried to do

list.add(new Ford());

this had to blow up – the target list could just as well have been a List<Toyota>, which would have been really bad.

—–

Ok, two questions now. First, how do I declare a List that can store any type of Car? Second, how is this unknown type genericising even useful?

The first one is really easy. All you have to do is declare it as a List of Cars:

List<Car> list = new ArrayList<Car>();

Now the type is known – it’s Car, which includes all its subtypes like Toyota and Ford, and you can add a Ford instance to the list – that works!

The second question is a bit harder to answer because we first have to realize the limitations of the genericised List. Specifically, the following will not compile:

List<Car> list = new ArrayList<Ford>();
...
Compile error:
incompatible types found : java.util.ArrayList<Main.Ford> required: java.util.List<Main.Car>

Even though Car has the inheritance relationship with Ford and Toyota, List<Car> does not share that same relationship with List<Ford> and List<Toyota>. In other words, List<Car> is not a superclass of List<Ford> and List<Toyota> (d-uh) so it cannot reference them. If you want a generic reference to all of these lists, such as passing any of these lists to a method, you have to resort to the even more generic List<? extends Car> variant:

public void doStuffWithListOfSomeCars(List<? extends Car> theList) // accepts List<Car>, List<Toyota> and List<Ford> 

In other words, the “? extends …” construct introduces a new form of polymorphism that’s really specific to generics. A little funky but still much better than List<Object>.