Demystifying Java Generics — gotchas and workarounds

extends Number>:public void testListAssignment() { List<Integer> integerList = new ArrayList<Integer>(); accept(integerList); // this now compiles} private void accept(List<? extends Number> numberList) { numberList.

add(new Double(1.

5)); // this now fails to compile}We’ve moved the compile failure to a different point.

The accept method signature means “a list of some unknown type that extends (or is) Number”.

It will now accept any List whose generic type is any class that extends Number, or is Number itself.

The line where we attempt to add a value will not compile, because the compiler is unable to determine if the added object is of the correct type, because the type is unknown (?).

It still doesn’t compile because if it did then we would be able to store a Double in the List<Integer> which is what we wanted to avoid.

In fact, even if we try to add an Integer it will not compile, or anything at all.

This gives us some rules:You cannot add anything to a List<?>You cannot add anything to a List<?.extends SomeClass>Retrieved objects from List<?.extends SomeClass> are of type SomeClassIn effect, a wildcard or wildcard extending something else — the list becomes read-only.

This is a direct consequence of the lack of runtime checks.

The only thing you can do that is useful is extract the values — if it is List<?.extends SomeClass> then all the values are guaranteed to be at least of the type SomeClass — so this will work:private void accept(List<? extends Number> numberList) { for (Number number : numberList) { System.



doubleValue()); }}Using the wildcard — <?.super SomeClass>This means “some unknown type, that is SomeClass or a super class of SomeClass”.

To demonstrate:public void testListAssignment() { List<Number> numberList = new ArrayList<Number>(); accept(numberList);}private void accept(List<? super Integer> inputList) { inputList.

add(new Integer(1)); // guaranteed to be fine Object object = inputList.

get(0);// return type is Object inputList.

add(new Object()); // compile failure – unbounded type}Operationally, this works the opposite way around to <?.extends Number> — you can add but retrieve operations are limited to returning Object.

The method will accept a List<Object>, a List<Number> and a List<Integer> — in all cases you can safely add an Integer, but not a Number or an Object.

In all three cases, the only thing you can say about what the get(…) method returns is that it is always assignable to Object.

This gives us the rules:you can add SomeClass instances to List<?.super SomeClass>retrieved objects from List<?.super SomeClass> are of type ObjectMore generally if you have a producer of SomeClass (or children), and a consumer of SomeClass (or children) that has a method like the above “accept”, then the rules of thumb to remember:producer takes <?.super SomeClass>consumer takes <?.extends SomeClass>Notes about wildcards:If the producer instead returns a list, then please, return it without any wildcards — methods that return wildcards should be discouraged as they will end up “polluting” code.

The scope of a wildcard should be kept to a minimumThe syntax for <?.super SomeClass> and <?.extends SomeClass> is inclusive — in both cases SomeClass itself is a legal typeThe word “extends” applies also when a class implements an interface, i.


<?. More details

Leave a Reply