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) {}
Related
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
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.
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
I have a class that can a list of take both a generic Number and a Double to perform a certain calculation.
This works fine:
public class Test<T extends Number> {
public void testGeneric(List<T> list){
doTest(list);
}
public void testExplicit(List<Double> list){
doTest(list);
}
public void doTest(List<? extends Number> testList){}
}
However, if the argument in question is a nested type, it doesn't compile anymore:
public class Test<T extends Number> {
public void testGeneric(List<List<T>> list){
doTest(list);
}
public void testExplicit(List<List<Double>> list){
doTest(list);
}
public void doTest(List<List<? extends Number>> testList){}
}
Note that the functionality of the list doesn't matter, the second List type can be anything generic, for instance a wrapper around the T type. I don't really see why there should be any difference. Is there a way around this?
Thanks!
As you apparently have realized, a List<Double> is not a subtype of List<Number>; you need the wildcards to make the inheritance "propagate" into the generic types: a List<Double> is a subtype of List<? extends Number>. However, the wildcards must go all the way from the outermost level: a List<X> is a subtype of List<List<? extends Number>> only if X is exactly a List<? extends Number>. If you want to accept other subtypes of List<? extends Number>, such as List<Double>, you need List<? extends List<? extends Number>>.
List<? super MyClass> parentsOfMyclass
List<? extends MyClass> childsOfMyclassFirst
List<T extends MyClass> childsOfMyclassSecond
Please explain me what types valid to add in these collections ? I can add instance of MyClass to all of these collections?
List<? super MyClass> - contains something that is superclass of MyClass, which means that you can put only MyClass and it superclasses here.
List<? extends MyClass> - contains something that extends MyClass, which means that you can't add anything here (because you don't know which subclass is used exactly). Although you can say for sure, that items are MyClass (or subclasses).
List<T extends MyClass> - contains T, that is subclass of MyClass. Can only add T and subclasses of it.
You can't actually use the last option like that. It can be used in class declaration, but not in variable declaration.
static class MyClass {}
static class My2ndClass extends MyClass {}
static class ListExt<T extends MyClass> {}
public static void main(String[] args) {
List<? super MyClass> parentsOfMyclass;
List<? extends MyClass> childsOfMyclassFirst;
List<T extends MyClass> childsOfMyclassSecond; //error
ListExt<? extends MyClass> extList;
childsOfMyclassFirst.add(new MyClass()); //error
parentsOfMyclass.add(new MyClass());
extList.add(new MyClass()); // error
}
You can add instance of MyClass only in the first case (with super). ? super MyClass makes the list a consumer, it can namely receive objects of type MyClass. In the second case the list cannot receive anything, it is a producer, a read-only list. In the third case you can add a subtype of MyClass if you make downcasting before: .add((T)object); and it of couse it does not throw an exception.
The correct syntax of the third case would be List<T> list and T extends MyClass should be in the class declaration.