I have a couple of questions about generic wildcards in Java:
What is the difference between List<? extends T> and List<? super T>?
What is a bounded wildcard and what is an unbounded wildcard?
In your first question, <? extends T> and <? super T> are examples of bounded wildcards. An unbounded wildcard looks like <?>, and basically means <? extends Object>. It loosely means the generic can be any type. A bounded wildcard (<? extends T> or <? super T>) places a restriction on the type by saying that it either has to extend a specific type (<? extends T> is known as an upper bound), or has to be an ancestor of a specific type (<? super T> is known as a lower bound).
The Java Tutorials have some pretty good explanations of generics in the articles Wildcards and More Fun with Wildcards.
If you have a class hierarchy A, B is a subclass of A, and C and D are both subclasses of B like below
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
Then
List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();
List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile
public void someMethod(List<? extends B> lb) {
B b = lb.get(0); // is fine
lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}
public void otherMethod(List<? super B> lb) {
B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
lb.add(new B()); // is fine, as we know that it will be a super type of A
}
A bounded wildcard is like ? extends B where B is some type. That is, the type is unknown but a "bound" can be placed on it. In this case, it is bounded by some class, which is a subclass of B.
Josh Bloch also has a good explanation of when to use super and extends in this google io video talk where he mentions the Producer extends Consumer super mnemonic.
From the presentation slides:
Suppose you want to add bulk methods to Stack<E>
void pushAll(Collection<? extends E> src);
– src is an E producer
void popAll(Collection<? super E> dst);
– dst is an E consumer
There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.
Collection<? extends MyObject>
means that it can accept all object who have IS- A relationship with MyObject (i.e. any object which is a type of myObject or we can say any object of any subclass of MyObject) or a object of MyObject class.
For example:
class MyObject {}
class YourObject extends MyObject{}
class OurObject extends MyObject{}
Then,
Collection<? extends MyObject> myObject;
will accept only MyObject or children of MyObject(i.e. any object of type OurObject or YourObject or MyObject, but not any object of superclass of MyObject).
In general,
If a structure contains elements with a type of the form ? extends E, we can get elements out of the structure, but we cannot put
elements into the structure
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");
To put elements into the structure we need another kind of wildcard called Wildcards with super,
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
Generic wildcards are created to make methods that operate on Collection more reusable.
For example, if a method has a parameter List<A>, we can only give List<A> to this method. It is a waste for this method's funtion under some circumstances:
If this method only reads objects from List<A>, then we should be allowed to give List<A-sub> to this method. (Because A-sub IS a A)
If this method only inserts objects to List<A>, then we should be allowed to give List<A-super> to this method. (Because A IS a A-super)
learn by example:
consider the sort() method in Collections class which use both extends and super:
public static <T extends Comparable<? super T>> void sort(List<T> list){...}
so
why <T extends Comparable<...>>: becuase we need list items (T) to be a subclass of the Comparable interface.
why Comparable<? super T>: becuase we allow the Comparable type
to be a Comparable of any super type of T.
Consider
interface Comparable<T>{
public int compareTo(T o);
}
public static <T extends Comparable<? super T>> void sort(List<T> list){...}
public static <T extends Comparable<T>> void sort2(List<T> list){...}
class A implements Comparable<A>{
#Override
public int compareTo(A o) {
...
}
}
class B extends A {
}
List<A> listA = new ArrayList<>();
List<B> listB = new ArrayList<>();
sort(listA); //ok
sort(listB); //ok
sort2(listA); //ok
sort2(listB); //Error
Related
In this example:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile() fails to compile with:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
while compiles() is accepted by the compiler.
This answer explains that the only difference is that unlike <? ...>, <T ...> lets you reference the type later, which doesn't seem to be the case.
What is the difference between <? extends Number> and <T extends Number> in this case and why doesn't the first compile?
By defining the method with the following signature:
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
and invoking it like:
compiles(new HashMap<Integer, List<Integer>>());
you're matching T against the type you're providing.
In the jls §8.1.2 we find, that (interesting part bolded by me):
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments. All of these parameterized types share the same class at run time.
In other words, the type T is matched against the input type and assigned Integer. The signature will effectively become static void compiles(Map<Integer, List<Integer>> map).
When it comes to doesntCompile method, jls defines rules of subtyping (§4.5.1, bolded by me):
A type argument T1 is said to contain another type argument T2, written T2 <= T1, if the set of types denoted by T2 is provably a subset of the set of types denoted by T1 under the reflexive and transitive closure of the following rules (where <: denotes subtyping (§4.10)):
? extends T <= ? extends S if T <: S
? extends T <= ?
? super T <= ? super S if S <: T
? super T <= ?
? super T <= ? extends Object
T <= T
T <= ? extends T
T <= ? super T
This means, that ? extends Number indeed contains Integer or even List<? extends Number> contains List<Integer>, but it's not the case for Map<Integer, List<? extends Number>> and Map<Integer, List<Integer>>. More on that topic can be found in this SO thread. You can still make the version with ? wildcard work by declaring, that you expect a subtype of List<? extends Number>:
public class Example {
// now it compiles
static void doesntCompile(Map<Integer, ? extends List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
In the call:
compiles(new HashMap<Integer, List<Integer>>());
T is matched to Integer, so the type of the argument is a Map<Integer,List<Integer>>. It's not the case for the method doesntCompile: the type of the argument stays Map<Integer, List<? extends Number>> whatever the actual argument in the call; and that is not assignable from HashMap<Integer, List<Integer>>.
UPDATE
In the doesntCompile method, nothing prevents you to do something like this:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
map.put(1, new ArrayList<Double>());
}
So obviously, it cannot accept a HashMap<Integer, List<Integer>> as the argument.
Simplied example of demonstration. Same example can be visualize like below.
static void demo(List<Pair<? extends Number>> lst) {} // doesn't work
static void demo(List<? extends Pair<? extends Number>> lst) {} // works
demo(new ArrayList<Pair<Integer>()); // works
demo(new ArrayList<SubPair<Integer>()); // works for subtype too
public static class Pair<T> {}
public static class SubPair<T> extends Pair<T> {}
List<Pair<? extends Number>> is a multi-level wildcards type whereas List<? extends Number> is a standard wildcard type .
Valid concrete instantiations of the wild card type List<? extends Number> include Number and any subtypes of Number whereas in case of List<Pair<? extends Number>> which is a type argument of type argument and itself has a concrete instantiation of the generic type.
Generics are invariant so Pair<? extends Number> wild card type can only accept Pair<? extends Number>>. Inner type ? extends Number is already covariant. You have to make the enclosing type as covariant to allow covariance.
I'd recommend you to look in documentation of generic wildcards especially guidelines for wildcard use
Frankly speaking your method #doesntCompile
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
and call like
doesntCompile(new HashMap<Integer, List<Integer>>());
Is fundamentally incorrect
Let's add legal implementation:
static void doesntCompile(Map<Integer, List<? extends Number>> map) {
List<Double> list = new ArrayList<>();
list.add(0.);
map.put(0, list);
}
It is really fine, because Double extends Number, so put List<Double> is absolutely fine as well as List<Integer>, right?
However, do you still suppose it's legal to pass here new HashMap<Integer, List<Integer>>() from your example?
Compiler does not think so, and is doing his (its?) best to avoid such situations.
Try to do the same implementation with method #compile and compiler will obviously does not allow you to put a list of doubles into map.
static <T extends Number> void compiles(Map<Integer, List<T>> map) {
List<Double> list = new ArrayList<>();
list.add(10.);
map.put(10, list); // does not compile
}
Basically you can put nothing but List<T> that's why it's safe to call that method with new HashMap<Integer, List<Integer>>() or new HashMap<Integer, List<Double>>() or new HashMap<Integer, List<Long>>() or new HashMap<Integer, List<Number>>().
So in a nutshell, you are trying to cheat with compiler and it fairly defends against such cheating.
NB: answer posted by Maurice Perry is absolutely correct. I'm just not sure it's clear enough, so tried (really hope I managed to) to add more extensive post.
class SuperCl {}
class A extends SuperCl {}
class B extends SuperCl {}
static void method(Map<Integer, List<? extends SuperCl>> map) {}
public static void main(String[] args) {
method(new HashMap<Integer, List<A>>()); //ERROR
}
The compile time error is that the types are incompatible:
Map<Integer, List<A>> cannot be converted to Map<Integer, List<? extends SuperCl>>
How can I fix it and where does the error come from?
I assume it comes from the "method" being static.
EDIT: I changed the map implementation to HashMap (copy error) - this should not change anything
Change your method to
static <T extends SuperCl> void method(Map<Integer, List<T>> map) {
}
Edit: The error mainly comes from the use of a nested generic. If you would have something like
static void method (List<? extends SuperC1> list) {
}
public static void main (String[] args) {
List<A> list = new ArrayList<>();
method(list);
}
you would not get a compile time error because A satisfies ? extends SuperCl.
A HashMap<Integer, List<A>> isn't a Map<Integer, List<? extends SuperCl>>, because you can add any type of List<? extends SuperCl> to the latter.
For example:
Map<Integer, List<A>> original = new HashMap<Integer, List<A>>();
// Raw types to intentionally break the type system.
Map<Integer, List<? extends SuperCl>> map = (Map) original;
List<B> listOfB = new ArrayList<>();
listOfB.add(new B());
map.put(0, listOfB);
List<A> listOfA = original.values().iterator().next();
A item = listOfA.get(0); // ClassCastException.
If you could do that, you'd have been able to add a value that's not a List<A> to it. Hence it's not allowed.
You could change the type in the method signature to this, for example:
Map<Integer, ? extends List<? extends SuperCl>>
and that would be fine, because you can't put any value into that (other than literal null).
This works:
class SuperCl {}
class A extends SuperCl {}
class B extends SuperCl {}
static <T extends SuperCl> void method(Map<Integer, List<T>> map) {}
public static void main(String[] args) {
method(new HashMap<Integer, List<A>>()); //NO MORE ERROR
}
I simply moved the generics:
<T extends SuperCl>
to the static method declaration. This makes it verifiable at compile time. On the other hand, having that generic at the method argument is not compile time verifiable.
Generics are invariant. For parameterized types to be compatible, their type arguments must match exactly, unless one of them is a wildcard at the top level. Map<Integer, List<A>> is not a subtype of Map<Integer, List<? extends SuperCl>> because List<A> is not identical to List<? extends SuperCl>. Yes, List<A> is a subtype of List<? extends SuperCl>, but they are not identical, which is what is needed.
As you may know, List<Dog> is not a subtype of List<Animal>, even though Dog is a subtype of Animal. It's the same situation here. A subtype relationship of the type arguments does not lead to a subtype relationship of the parameterized types (that would be called "covariant"; Java array types are covariant, but generics are not).
One solution to this is to use a wildcard at the top level. For example, List<Dog> is a subtype of List<? extends Animal>. Similarly in your case, Map<Integer, List<A>> is a subtype of Map<Integer, ? extends List<? extends SuperCl>>. So you can declare your method as:
static void method(Map<Integer, ? extends List<? extends SuperCl>> map) {}
I was trying to solve a problem where I am not able to understand part of the answer.
Following is the class BackLister:
public class BackLister {
// INSERT HERE
{
List<T> output = new LinkedList<T>();
for (T t : input)
output.add(0, t);
return output;
}
}
The question asks which can be inserted at // INSERT HERE in the BackLister class to compile and run without error?
Following are the options:
A. public static <T> List<T> backwards(List<T> input)
B. public static <T> List<T> backwards(List<? extends T> input)
C. public static <T> List<T> backwards(List<? super T> input)
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)
I understand that that A and B are correct, as for for (T t : input) to work the elements in input should be of type T or subtype of T.
But I am not able to understand why D and E options are correct?
I understand the following:
public static <T> List<? extends T> backwards(List<T> input)
means that the return type should be a List of T or subclass
of T.
public static <T> List<? super T> backwards(List<T> input) means that the return type should be a List of T or
superclass of T.
Could somebody help me understand it?
There is difference between each of them and I'm going to explain most of them. Let's start with our example. I use this class hierarchy:
class Food {}
class Apple extends Food {}
class Orange extends Food {}
class RedApple extends Apple {}
List<Food> listFood = new ArrayList<>();
List<Apple> listApple = new ArrayList<>();
List<Orange> listOrange = new ArrayList<>();
List<RedApple> listRedApple = new ArrayList<>();
ow start with first one:
A. public static <T> List<T> backwards(List<T> input)
This method will only accept List<T> and return List<T> and you can not send listApple and return listRedApple. (however your return list can contain RedApple because it extends Apple but type of list must be List<Apple> and nothing else)
B. public static <T> List<T> backwards(List<? extends T> input)
You can send listRedApple and return listApple but you know that listRedApple is "? extend Apple" so in the method body java recognize T as Apple. Then if you use can add elements in listRedApple which sent as argument, you can add Apple in listRedApple which is not true!!! so compiler avoid it and give compile error. In B you can only read elements (and get it as T) but you can not add anything to it.
C. public static <T> List<T> backwards(List<? super T> input)
You can send listApple and then in method body you can add anything extends Apple because compiler see T as Apple and in a list of anything which is super of T, you can add anything extends Apple.
However this time, you can not read anything because you don't know its type except you get it as Object. (It is a list of "? super T")
As you see here there is difference between ? super and ? extend. one of them give you write access and other give you read access. This is the real use of wildcard.
D. public static <T> List<? extends T> backwards(List<T> input)
E. public static <T> List<? super T> backwards(List<T> input)
If you send listApple then you return List<? extends Apple> but you can's assign it to any of listFood or listApple or listRedApple because List<? extends Apple> maybe contain Apple or RedApple or something else and we can't assign it to any List<T> because then we can add T to that list and maybe T and ? extends T is not same. this is same for both D and E. You can assign it to List<? extends Apple> for 'E and List<? super Apple> for D and send them to a method which need them as parameter.
F. public static <? extends T> List<T> backwards(List<T> input)
G. public static <? super T> List<T> backwards(List<T> input)
Give compile errors because wildcard can not used like this.
I hope this help you.
If something is wrong, any comment is appreciated.
Options D and E are valid because there exist a super-subtype relationships among generic types that allow you to define a larger set of types that the method can accept or return.
Consequently, the following is valid (D):
public static <T> List<? extends T> backwards(List<T> input) {
return List.of();
}
// there exist a super-subtype relationships among List<? extends Number> and List<Long>
List<? extends Number> list = backwards(List.<Long>of(1L, 2L));
because the type Long is a member of the type family that the wildcard ? extends Number denotes (the family of types that are subtypes of Number and the type Number itself).
The next code snippet is also valid (E):
public static <T> List<? super T> backwards(List<T> input) {
return List.of();
}
List<? super Long> ints = backwards(List.<Long>of(1L, 2L));
because the type Long is a member of the type family that the wildcard ? super Long denotes (the family of types that are supertypes of Long and the type Long itself).
So, your understanding is correct.
Further reading.
Below image describes the sub-typing relation in Generics:
List<T> is a subtype of List<? super T>
Also List<T> is a subtype of List<? extends T>
This is why options D and E are correct.
You can refer the page : https://docs.oracle.com/javase/tutorial/java/generics/subtyping.html
Collections class has static method copy:
public static <T> void ...copy(List<? super T> dest, List<? extends T> src) {
//...
ListIterator<? super T> di=dest.listIterator();
ListIterator<? extends T> si=src.listIterator();
//...
}
Assumes I have hierarhy:
Apple.class -> Fruit.class (Apple is subtype of Fruit class)
Apple.class -> IMakeJuice.class (Apple is subtype of IMakeJuice interface)
and I execute:
List<Object> objList = new ArrayList<Object>();
List<Apple> appleList = new ArrayList<Apple>();
Collections.copy(objList, appleList);
Will T be cast to Fruit, IMakeJuice, Object or Apple?
It will be the most specific type which satisfies the bounds, i.e. T will be Apple.
Quoting https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html:
the inference algorithm tries to find the most specific type that works with all of the arguments.
As Andy Turner already answered, T will be Apple.
For other similar questions, your IDE can tell you. I use Eclipse, but I assume the others will do this too.
If you add the following to any .java file:
class Test {
void test() {
List<Object> objList = new ArrayList<Object>();
List<Apple> appleList = new ArrayList<Apple>();
Collections.copy(objList, appleList);
}
}
class Fruit {}
interface IMakeJuice {}
class Apple extends Fruit implements IMakeJuice {}
Then hover over copy, or click and press the F2 key, it will show the javadoc with T resolved, including the method definition:
<Apple> void java.util.Collections.copy(List<? super Apple> dest, List<? extends Apple> src)
This is also very useful for identifying which of many overloaded methods gets called, e.g. String.valueOf(1) and String.valueOf((short)1) both call the int version, while String.valueOf(1.0) call the double version.
According to what I've read, I think this can't be done, but I'd like to be sure.
I have a class OpDTO and several other *DTO extends OpDTO.
Then, I want to have a method to extract just certain elements from lists of these child DTOs, and return the extracted elements in another list:
public List<? extends OpDTO> getLastOp (List<? extends OpDTO> listDTOs) {
List<? extends OpDTO> last = new ArrayList<? extends OpDTO>(); //compile error: Cannot instantiate the type ArrayList<? extends OpDTO>
//processing
return last;
}
I want ult to be a list of elements of the same kind as elements in listDTOs, and use only OpDTO's methods, but it produces a compile error:
Cannot instantiate the type ArrayList<? extends OpDTO>
I also tried doing something like:
public <T> List<T> getLastOp (List<T> listDTOs) {
List<T> last = new ArrayList<T>();
//processing
return last;
}
But then I can't enforce elements in listDTOs to be a subclass of OpDTO, and can't instantiate T.
Any idea?
EDIT
It also occurred to me passing the type as parameter, then I can instantiate it. Would that be ok or is it some kind of bad practice?
private <T extends OpDTO> List<T> getLastOp (List<T> listDTOs, Class<? extends OpDTO> clazz) {
List<T> ult = new ArrayList<T>();
//processing
OpDTO op = clazz.newInstance();
//processing
ult.add((T) op);
op = listDTOs.get(i);
return ult;
}
List<? extends OpDTO> is a covariant view of List<T>; that means that any List<T> type can be converted to it, as long as the T matches.
You cannot create an instance of such a type; you can only use it as a variable or parameter type to hold existing instances of concrete generic types.
You need to create a generic method with a constraint that T must inherit your class:
public <T extends OpDTO> List<T> getLastOp (List<T> listDTOs) {