I have a class:
class Generic<T> {
List<List<T>> getList() {
return null;
}
}
When I declare a Generic with wildcard and call getList method, the following assignment is illegal.
Generic<? extends Number> tt = null;
List<List<? extends Number>> list = tt.getList(); // this line gives compile error
This seems odd to me because according to the declaration of Generic, it's natural to create a Generic<T> and get a List<List<T>> when call getList.
In fact, it require me to write assignment like this:
List<? extends List<? extends Number>> list = tt.getList(); // this one is correct
I want to know why the first one is illegal and why the second one is legal.
The example I give is just some sample code to illustrate the problem, you don't have to care about their meaning.
The error message:
Incompatable types:
required : List<java.util.List<? extends java.lang.Number>>
found: List<java.util.List<capture<? extends java.lang.Number>>>
This is a tricky but interesting thing about wildcard types that you have run into! It is tricky but really logical when you understand it.
The error has to do with the fact that the wildcard ? extends Number does not refer to one single concrete type, but to some unknown type. Thus two occurrences of ? extend Number don't necessarily refer to the same type, so the compiler can't allow the assignment.
Detailed explanation
The right-hand-side in the assignment, tt.getList(), does not get the type List<List<? extends Number>>. Instead each use of it is assigned by the compiler a unique generated capture type, for exampled called List<List<capture#1 extends Number>>.
The capture type List<capture#1 extends Number> is a subtype of List<? extends Number>, but it is not type same type! (This is to avoid mixing different unknown types together.)
The type of the left-hand-side in the assignment is List<List<? extends Number>>. This type does not allow subtypes of List<? extends Number> to be the element type of the outer list, thus the return type of getList can't be used as the element type.
The type List<? extends List<? extends Number>> on the other hand does allow subtypes of List<? extends Number> as the element type of the outer list. So that is the right fix for the problem.
Motivation
The following example code demonstrates why the assignment is illegal. Through a sequence of steps we end up with a List<Integer> which actually contains Floats!
class Generic<T> {
private List<List<T>> list = new ArrayList<>();
public List<List<T>> getList() {
return list;
}
}
// Start with a concrete type, which will get corrupted later on
Generic<Integer> genInt = new Generic<>();
// Add a List<Integer> to genInt.list. This is not necessary for the
// main example but migh make things a little clearer.
List<Integer> ints = List.of(1);
genInt.getList().add(ints);
// Assign to a wildcard type as in the question
Generic<? extends Number> genWild = genInt;
// The illegal assignment. This doesn't compile normally, but we force it
// using an unchecked cast to see what would happen IF it did compile.
List<List<? extends Number>> list =
(List<List<? extends Number>>) (Object) genWild.getList();
// This is the crucial step:
// It is legal to add a List<Float> to List<List<? extends Number>>.
// list refers to genInt.list, which has type List<List<Integer>>.
// Heap pollution occurs!
List<Float> floats = List.of(1.0f);
list.add(floats);
// notInts in reality is the same list as floats!
List<Integer> notInts = genInt.getList().get(1);
// This statement reads a Float from a List<Integer>. A ClassCastException
// is thrown. The compiler must not allow us to end up here without any
// previous type errors or unchecked cast warnings.
Integer i = notInts.get(0);
The fix that you discovered was to use the following type for list:
List<? extends List<? extends Number>> list = tt.getList();
This new type shifts the type error from the assignment of list to the call to list.add(...).
The above illustrates the whole point of wildcard types: To keep track of where it is safe to read and write values without mixing up types and getting unexpected ClassCastExceptions.
General rule of thumb
There is a general rule of thumb for situations like this, when you have nested type arguments with wildcards:
If the inner types have wildcards in them, then the outer types often need wildcards also.
Otherwise the inner wildcard can't "take effect", in the way you have seen.
References
The Java Tutorial contains some information about capture types.
This question has answers with general information about wildcards:
What is PECS (Producer Extends Consumer Super)?
This question already has answers here:
Generics <? super A> doesn't allow superTypes of A to be added to the list
(2 answers)
Closed 4 years ago.
I am trying out a easy to understand example about contravariance in Java and having a issue understanding.
In the below example I have List<? super CarBill> list1 . My understanding is i should be able to add an object of any superclass of CarBill. By that logic i should be able to add objects of Bill class to it too right ?
I get a compilation error.
package Generics;
import java.util.ArrayList;
import java.util.List;
public class VarianceTests {
static class Bill{
String vName;
String type;
Bill(String vName){
this.vName=vName;
}
Bill(String vName,String type){
this.vName=vName;
this.type=type;
}
}
static class CarBill extends Bill{
String name;
CarBill(String name)
{
super(name,"Car");
}
}
static class Car<T extends Bill> {
T car;
Car(T car){
this.car=car;
}
String getNameOfCar() {
return car.vName;
}
}
public static void main(String args[]) {
CarBill cBill = new CarBill("Baleno");
Bill bill=new Bill("Whatever");
Car car = new Car(bill); //cBill is valid too as Car accepts <? extends Bill>
List<? super CarBill> list1 = new ArrayList<>();
list1.add(cBill);
list1.add(bill);
}
public void acceptListOfCars(List<? extends Bill> list1) {
Bill b = list1.get(0); //Valid syntax
}
}
Your understanding is mistaken.
List<? super CarBill> means that the list can be a list of any super class of CarBill or CarBill itself. It could be List<Object>, it could be List<Bill>, it could even be List<CarBill>. Which one is it actually? We don't know.
Therefore, you can't add a Bill to a List<? super CarBill> because what if the list is actually a List<CarBill>? You can't add a Bill to a List<CarBill>.
In other words, you can only add CarBill or subclasses of CarBill into a List<? super CarBill>.
If your intention is to create a list that can store any type of Bill, you can create a List<Bill>.
This post might help as well.
Not quite.
Let's start with this code:
List<Integer> listOfInts = new ArrayList<Integer>();
List<Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
The above code won't in fact compile; the second line is an invalid assignment. Your line of thinking would say: But.. why? Number is a supertype of Integer, so, a list of integers is trivially also a list of numbers, no? but then the third line shows why this line of reasoning is incorrect. Java will NOT let you write the above code. What you CAN write is this: The same thing, but this time we tweak the second line:
List<Integer> listOfInts = new ArrayList<Integer>();
List<? extends Number> listOfNumbers = listOfInts;
listOfNumbers.add(5.5D); // a double
int i = listOfInts.get(0); // uhoh!
This time, you get a compiler error on the third line: You cannot add a double to this list. But, if you read from it, you'd get numbers out (not objects). This is all good: The above snippet of code should never compile no matter what we try because it tries to add doubles to a list of ints.
The point is: List<? extends Number> does not mean: "This list contains numbers, or any subtypes thereof". No; just like List x = new ArrayList() is legal java, List<Number> means 'this list contains numbers or any subtypes thereof' because any instance of any subtype of number can itself be used as a Number. List<? extends Number> means: This is a list restrained to contain only instances of some specific type, but which type is not known. What IS known, is that whatever that type is, it's either Number or some subtype thereof.
Hence, you can't add ANYTHING to a List<? extends Number>.
For super, a similar story:
List<? super CarBill> means: This is a list that is restricted to contain only instances of some specific type, but which type is not known. What IS known, is that, whatever type it is, it is either CarBill or some SUPERtype thereof.
The upside of doing this, is that you can add CarBill instances to a List<? super CarBill> variable. When you read from it, you'll get objects out.
My understanding is i should be able to add an object of any superclass of CarBill
No.
A List<? super CarBill> is not a list that will accept objects of any supertype of CarBill. It's a list that will accept objects of some particular supertype of CarBill, but which supertype it is is unknown.
You can add any object of type CarBill, because that is guaranteed be a subtype of type ?. But a supertype of CarBill is not guaranteed to be a subtype of ?.
For instance:
List<? super CarBill> myList = new ArrayList<Bill>();
Object o = "Anything";
Object is a supertype of CarBill. So if you could add any supertype of CarBill to the list, you would be able to add o to the list, which would mean you could add anything to the list.
I thought I have a reasonable grasp of generics. For example, I understand why
private void addString(List<? extends String> list, String s) {
list.add(s); // does not compile
list.add(list.get(0)); // doesn't compile either
}
Does not compile. I even earned some internet karma with the knowledge.
But I'd think by the same argument this shouldn't compile:
private void addClassWildcard(List<Class<? extends String>> list, Class<? extends String> c) {
list.add(c);
list.add(list.get(0));
}
Nor should this:
private void addClass(List<Class<? extends String>> list, Class<String> c) {
list.add(c);
list.add(list.get(0));
}
But both compile. Why? What is the difference to the example from the top?
I'd appreciate an explanation in common English as well as a pointer to the relevant parts of the Java Specification or similar.
The second case is safe because all instances of Class<String> are instances of Class<? extends String>.
There is nothing unsafe about adding an instance of Class<? extends String> to a List<Class<? extends String> - you will get back an instance of Class<? extends String> using get(int), iterator() etc - so it's allowed.
In a sense the wildcard inside Class gets only considered when an instance of that is actually encountered. Consider the following examples (switching from String to Number since String is final).
private void addClass(List<Class<? extends Number>> list, Class<Number> c) {
list.add(c);
list.add(list.get(0));
}
private void tryItSubclass() {
List<Class<Integer>> ints = new ArrayList<>();
addClass(ints, Number.class); // does not compile
}
Here ints can only ever contain instances of Class<Integer> but Number.class is also a Class<? extends Number> with ? captured as Number so the two types are not compatible.
private void tryItBound() {
List<Class<Number>> ints = new ArrayList<>();
addClass(ints, Number.class); // does not compile
}
Here ints can only ever contain instances of Class<Number> but Integer.class is also a Class<? extends Number> with ? captured as Integer so the two types are not compatible.
private void tryItWildcard() {
List<Class<? extends Number>> ints = new ArrayList<>();
addClass(ints, Number.class); // does compile
Class<? extends Number> aClass = ints.get(0);
}
The first case is unsafe because - were there a hypothetical class which extended String (which there isn't, because String is final; however, generics ignore final), a List<? extends String> might be a List<HypotheticalClass>. As such, you can't add a String to a List<? extends String>, because you expect everything in that list to be an instance of HypotheticalClass:
List<HypotheticalClass> list = new ArrayList<>();
List<? extends String> list2 = list;
list2.add(""); // Not allowed, but pretend it is.
HypotheticalClass h = list.get(0); // ClassCastException.
This has to do with capture conversion. Andy's answer is great but it doesn't explain how the specification works. My answer here is long because, well, this is a pretty dense part of the JLS, but I don't see it explained much and it's not that difficult if you walk through it step-by-step.
Capture conversion is a process whereby the compiler takes a type with wildcards and replaces (some of) the wildcards with types which are not wildcards.
The supertypes of a parameterized type with wildcards are the supertypes of that type after capture conversion:
4.10.2. Subtyping among Class and Interface Types
Given a generic type declaration C<F1,...,Fn> (n > 0), the direct supertypes of the parameterized type C<R1,...,Rn> where at least one of the Ri (1 ≤ i ≤ n) is a wildcard type argument, are the direct supertypes of the parameterized type C<X1,...,Xn> which is the result of applying capture conversion to C<R1,...,Rn>.
The types of the members (including methods) of a parameterized type with wildcards are the types of the members of that type after capture conversion:
4.5.2. Members and Constructors of Parameterized Types
Let C be a generic class or interface declaration with type parameters A1,...,An, and let C<T1,...,Tn> be a parameterization of C where, for 1 ≤ i ≤ n, Ti is a type (rather than a wildcard). Then:
[skipped for irrelevance]
If any of the type arguments in the parameterization of C are wildcards, then:
The types of the fields, methods, and constructors in C<T1,...,Tn> are the types of the fields, methods, and constructors in the capture conversion of C<T1,...,Tn>.
So how does capture conversion work?
Suppose we are given the following class declaration (chosen to illustrate some parts of the process more completely):
class C<V, W extends List<V>> {
void m(V v, W w) {
}
}
And the following use of this type:
C<Number, ?> c = new C<>();
Double tArg = 1.0;
List<Number> uArg = new ArrayList<>();
c.m(tArg, uArg);
How do we determine the type of c.m for the purpose of determining if the argument types may be assigned to the parameter types?
Well, to start with, as stated above, the parameter types of c.m are the parameter types of m in the capture conversion of C<Number, ?>:
5.1.10. Capture Conversion
Let G name a generic type declaration with n type parameters A1,...,An with corresponding bounds U1,...,Un.
For this example:
G is C.
A1 is V with bound U1 which is Object.
A2 is W with bound U2 which is List<V>.
There exists a capture conversion from a parameterized type G<T1,...,Tn> to a parameterized type G<S1,...,Sn>...
For this example, G<T1,...,Tn> is C<Number, ?>:
T1 is Number.
T2 is ?.
..., where, for 1 ≤ i ≤ n:
If Ti is a wildcard type argument of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type.
If Ti is a wildcard type argument of the form ? extends Bi, then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1:=S1,...,An:=Sn]) and whose lower bound is the null type.
glb(V1,...,Vm) is defined as V1 & ... & Vm.
Ui[A1:=S1,...,An:=Sn] is the bound of Ai (the type parameter) with the substitution of each type argument for each corresponding type parameter. (This is why I declared C with a type parameter whose bound references another type parameter: because it illustrates what this part does.)
In our example, for T2 (which is ?), S2 is a fresh type variable whose upper bound is U2 (which is List<V>) with the substitution of Number for V.
S2 is therefore a fresh type variable whose upper bound is List<Number>.
For simplicity, I'm going to ignore the case where we have a bounded wildcard, but a bounded wildcard is essentially just capture converted to a fresh type variable whose bound is BoundOfWildcard & BoundOfTypeParameter. Also, if a wildcard has a lower bound (super), then the fresh type variable has the lower bound too.
If Ti is not a wildcard, then:
Otherwise, Si = Ti.
So in our example, S1 is just T1 which is Number.
And that:
Capture conversion is not applied recursively.
which we'll get to later.
We now know that:
S1 is Number.
S2 is some type variable FRESH extends List<Number> which the compiler's just created.
Therefore, the capture conversion of C<Number, ?> is C<Number, FRESH>.
Now we can actually answer the question: are Double and List<Number> assignable to Number and FRESH extends List<Number>, respectively? In the former case, yes. In the latter case, no.
This is for the same reasons that the expression wouldn't compile if we declared a type variable in this way ourselves:
static <FRESH extends List<Number>> void n() {
C<Number, FRESH> c = new C<>();
Double tArg = 1.0;
List<Number> uArg = new ArrayList<>();
c.m(tArg, uArg);
}
The supertypes of a type variable are:
The direct supertypes of a type variable are the types listed in its bound.
Therefore, List<Number> may not be assigned to FRESH because List<Number> is a a supertype of FRESH.
By analogy, we could also declare a class this way:
class Fresh extends List<Number> {}
C<Number, Fresh> c = new C<>();
Double tArg = 1.0;
List<Number> uArg = new ArrayList<>();
c.m(tArg, uArg);
That might be more familiar, and isn't really all that different with respect to how the relationship between types works in this case.
In other words, in our original example:
C<Number, ?> c = new C<>();
Double tArg = 1.0;
List<Number> uArg = new ArrayList<>();
c.m(tArg, uArg);
// ^^^^ this
is just a more complicated version of this:
Object o = ...;
String s = o; // Error: attempting to assign a supertype to its subtype.
and (at the end of the day) doesn't compile for roughly the same reason.
In Summary
Capture conversion takes wildcards and turns them in to type variables (temporarily). After that, it's just the regular rules of subtyping that cause these errors.
So for example, given the code in the question:
private void addString(List<? extends String> list, String s) {
list.add(s); // does not compile
list.add(list.get(0)); // doesn't compile either
}
While viewing the expression list.add(s), the compiler sees something like this:
private <CAP#1 extends String>
void addString(List<? extends String> list, String s) {
((List<CAP#1>) list).add( s );
list.add(list.get(0));
}
The error produced is as follows:
error: no suitable method found for add(String)
list.add(s); // does not compile
^
method Collection.add(CAP#1) is not applicable
(argument mismatch; String cannot be converted to CAP#1)
method List.add(CAP#1) is not applicable
(argument mismatch; String cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
CAP#1 extends String from capture of ? extends String
In other words, the compiler found methods add(CAP#1) and String is inconvertible to the type variable CAP#1.
While viewing the expression list.add(list.get(0)), the compiler sees something like this:
private <CAP#1 extends String, CAP#2 extends String>
void addString(List<? extends String> list, String s) {
list.add(s);
((List<CAP#2>) list).add( ((List<CAP#1>) list).get(0) );
}
The error produced is as follows:
error: no suitable method found for add(CAP#1)
list.add(list.get(0)); // doesn't compile either
^
method Collection.add(CAP#2) is not applicable
(argument mismatch; String cannot be converted to CAP#2)
method List.add(CAP#2) is not applicable
(argument mismatch; String cannot be converted to CAP#2)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends String from capture of ? extends String
CAP#2 extends String from capture of ? extends String
In other words, the compiler found that list.get(0) returns CAP#1 and found methods add(CAP#2) but CAP#1 is inconvertible to CAP#2.
(Source for errors.)
So why do List<Class<?>> and other similar types work?
Recall that:
Otherwise, [if Ti is not a wildcard type], Si = Ti.
And that:
Capture conversion is not applied recursively.
So if Ti is a parameterized type like Class<?>, then Si is just Class<?>. Also, since capture conversion is not applied recursively, the algorithm just stops after converting T1,...,Tn to S1,...,Sn. The new type is not capture-converted and the bounds of the fresh type variables are not capture-converted.
We can also verify that this is indeed what the compiler does by causing some interesting errors:
Map<?, List<?>> m = new HashMap<>();
List<?> list = new ArrayList<>();
list.add(m);
This produces the following error:
error: no suitable method found for add(Map<CAP#1,List<?>>)
list.add(m);
^
[…]
(Source.)
Note that the type argument List<?> in the Map type capture converts to itself.
And another:
Map<?, ? extends List<?>> m = new HashMap<>();
List<?> list = new ArrayList<>();
list.add(m);
This produces the following error:
error: no suitable method found for add(Map<CAP#1,CAP#2>)
list.add(m);
^
[…]
where CAP#1,CAP#2,CAP#3 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends List<?> from capture of ? extends List<?>
CAP#3 extends Object from capture of ?
(Source.)
Note that this time, while ? extends List<?> is capture-converted, the bound List<?> is not.
Finally
The answer to the question as-stated is that the wildcard in List<? extends String> is capture-converted to a fresh type variable but the wildcard in List<Class<? extends String>> is not.
Your example misses the fact (at least I think so), that (going to Integer and Number for existing examples) List<Class<Integer>> is no valid instance of List<Class<? extends Number>>.
So, this does not compile:
public static void main(String[] args) {
List<Class<Integer>> intClasses = new LinkedList<>();
addClass(intClasses, Number.class); // compiler error
}
private static void addClass(List<Class<? extends Number>> list, Class<Number> c) {
list.add(c);
list.add(list.get(0));
}
This question already has answers here:
Difference between <? super T> and <? extends T> in Java [duplicate]
(14 answers)
Closed 8 years ago.
This question may have been asked before in different formats but I just couldn't find an appropriate answer.
Whats the difference between these codes:
FIRST CODE
ArrayList<? super Number> list=new ArrayList<>(Arrays.asList(1));
SECOND CODE
ArrayList<? extends Number> list=new ArrayList<>(Arrays.asList(1));
Both statement compiles . Please justify why the former compiles . It shows Integer extends Number and Integer super Number too.
EDIT::
The answer I was looking forward to is:
new ArrayList(Collection<? extends E>) hence both of them compile.
In that way this isn't a duplicate question as the referred answer doesn't describe that.
<? super Number> means that the type is any superclass of Number.
<? extends Number> means that the type is any subclass of Number.
They'll compile to the same thing, but it depends what types you're actually going to do with your List.
If you have a List<? super Number>, you can put into it anything that is a Number, but the compiler can't guarantee that what you get out of it will be a Number. Whereas if you have a List<? extends Number>, then anything you get out will definitely inherit from Number.
The compiler will object if try and pass a List<? super Number> to a method expecting a List<? extends Number>, because the former might have stuff in that is not a Number.
The compiler will object if try and pass a List<? extends Number> to a method expecting a List<? super Number>, because the former can't necessarily receive elements that the latter can.
They both execute the same way, but the compiler will prevent them from compiling in different ways.
public class NewClass {
private List<? super Number> supers;
private List<? extends Number> extenders;
public NewClass() {
supers = new ArrayList<>();
extenders = new ArrayList<>();
}
public void addSupers() {
supers.add(new Integer("5"));
}
public void addExtenders() {
// This won't compile
extenders.add(new Integer("5"));
}
public void getSupers() {
// this won't compile
Number number = supers.get(1);
}
public void getExtenders() {
Number number = extenders.get(1);
}
}
In the supers add case, you can safely add Integers as Integers have a super class Number. You can't do that in the extenders case because the extenders hold a particular subclass of Number, which in this case is determined by a wild card. In short, the extenders item is generally assumed to hold all the same sub-class of Number.
In the supers get case, you cannot safely pull out a Number because for some types, might not be Numbers but rather super-classes of Number, so the "down cast" is dangerous. However, in the extenders scenario, all the contents are assured to be sub-classes of Number so they all can safely be cast to Number.
I am reading through a question where the signature of the method is given below
public static <E extends CharSequence> List<? super E> doIt(List<E> nums)
I am unable to decode the syntax. I am very fresh in generics and unable to understand
this part. Doesn't the first part <E extends CharSequence> tells what E should be, both
for as an argument and as a return type. But i do see List<? super E>, this defines the
bounds for the return type. Can someone help me understand this with an example?
Thanks.
<E extends CharSequence>
tells that E will be a subtype of CharSequence. This tells the compiler that the type argument that will be passed to this method will either be a CharSequence or a sub type of that type. This type of bound is known as a parameter bound. I have written an article on this topic, you can check it out if you like.
List<? super E>
tells that this method will return a List of elements whose type will be either E or its super type.
So, all of the following types could be returned from your doIt method -
// trivial one.
return new ArrayList<E>();
// If F is a super type of E, then the following line is valid too.
return new ArrayList<F>();
// The following will also be valid, since Object is a super type of all
// other types.
return new ArrayList<Object>();
List<? super E> - this is usually known as a contravariance. Check this out.