Do you know the difference in Java generics between <? super E>
and <? extends E>
? If you have any doubt, read on.
In Java, classes and interfaces can have type parameters, like a List of Numbers instead of just a List:
List list = new ArrayList();
With the diamond notation <>
, the Java 7 compiler uses type inference to deduce the argument type so you can abbreviate instantiation to new ArrayList<>()
.
You can also type the variable that holds the reference to the ArrayList
instance as a List
, a Collection
, or a Serializable
.
Let’s now say you have a pair of methods that take a List of Numbers. The first method printNumbers()
gets Numbers out of the List:
public void printNumbers(List list) { for (Number number : list) { System.out.print(number); System.out.print(", "); } }
The second method, fillNumbers()
, adds Numbers to the List:
public void fillNumbers(List list) { Number n = Integer.MAX_VALUE; list.add(n); list.add(3); }
The method getting Numbers out of the List could just as easily work with a List of Integers or a List of Floats:
List floats = new ArrayList(); floats.add(Float.valueOf(1.2f)); printNumbers(floats);
What we really want though is a List
, not a List
:
List floats = new ArrayList(); floats.add(Float.valueOf(1.2f)); printNumbers(floats);
But that causes the compiler to fail with:
The method printNumbers(List) in the type Test is not applicable for the arguments (List)
To use a List, the method must be typed with List<? extends Number>
.
public void printNumbers(List list)
The ? extends
tells the compiler that we want to use some unknown (the ?
) subtype of Number. We explicitly constrain the wildcard (the ?
) to represent the unknown subtype of Number. We say the method is a covariant use of the List of Numbers; it works with any List of any subtype of Number.
On the other hand, you should be able to give the method adding Numbers to the List any old List of Objects:
List objects = new ArrayList(); fillNumbers(objects);
That does not compile:
The method fillNumbers(List) in the type GenericsSuperExtendsDemo is not applicable for the arguments (List)You get this to work with List<? super Number>
:
public void fillNumbers(List list)
The ? super Number
is an explicitly constrained wildcard that represents some unknown supertype of Number. We call that one a contravariant use of the List of Numbers and it works for any List of any supertype of Number.Here is the complete example:
package test;import java.util.ArrayList;import java.util.List;public class GenericsSuperExtendsDemo { public static void main(String[] args) { new GenericsSuperExtendsDemo().go(); } public void go() { System.out.println("Hello World!"); List numbers = new ArrayList(); fillNumbers(numbers); printNumbers(numbers); { List floats = new ArrayList(); floats.add(Float.valueOf(1.2f)); printNumbers(floats); } { List floats = new ArrayList(); floats.add(Float.valueOf(1.2f)); printNumbers(floats); } { List integers = new ArrayList(); printNumbers(integers); } { List objects = new ArrayList(); fillNumbers(objects); } } public void printNumbers(List list) { for (Number number : list) { System.out.print(number); System.out.print(", "); } } public void fillNumbers(List list) { Number n = Integer.MAX_VALUE; list.add(n); list.add(3); }}
The inspiration for this article is a paper presented at OOPSLA 2016 titled “Java and Scala’s Type Systems are Unsound.”Happy Coding,Gary Gregory
It looks like the type parameter got lost (due to being mistaken for HTML?) on some of the generics, for example in “What we really want though is a List, not a List”.
LikeLike
Thank you very much! very clear explanation.You make a good teacher.
LikeLike
“public void fillNumbers(List list) {
Number n = Integer.MAX_VALUE;
numbers.add(n);
numbers.add(3);
}”
Think that shold be:
list.add(n);
list.add(3);
LikeLike
“What we really want though is a List, not a List:”
A list of Floats, not a list of Numbers perhaps?
LikeLike
You’re right! Fixed. Thank you Chris!
LikeLiked by 1 person
Thanks – really handy article – most appreciated.
LikeLike
public void printNumbers(List list) {
for (Object number : list) {
System.out.print(number);
System.out.print(“, “);
}
}
LikeLike