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.
Related
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)?
After looking into Java's Collection class (OpenJDK 8_update40), I found the following method:
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
Iterator<? extends T> i = coll.iterator();
T candidate = i.next();
while (i.hasNext()) {
T next = i.next();
if (next.compareTo(candidate) > 0)
candidate = next;
}
return candidate;
}
I don't fully understand the use of generic types here. As far as I understand has T to be a subtype of Object which also has to implement the Comparable interface which is also parameterized via a generic parameter. The parameter of Comparable states that is have to be some supertype of T. Due to that we have some kind of recursive type definition.
But here is my question: As far as I know every type in Java is a subtype of Object, so why do they specify
it within the definition of T?
This is for backwards compatibility reasons.
When you use a generic type and this generic type has lower bounds, such as:
<T extends Foo & Bar> void someMethod(T xxx)
then the runtime signature of someMethod will be:
void someMethod(Foo xxx)
(well, OK, the argument name is not there, but you get the picture).
Now, Collections.max() was defined before JDK 5; and its signature was:
public static Object max(Collection coll)
which, in Java 5, could be translated as:
public static Object max(Collection<Object> coll)
The thing is that the return value of max cannot be a Comparable...
Of course, in this case, more difficulties are added:
the second lower bound it itself a generic type;
moreover Comparable is a "consumer" in the PECS way (hence Comparable<? super T>);
the Collection passed as an argument can have any type which is either T or anything extending T, hence ? extends T; we don't care about the actual type, only that the Collection is guaranteed to return something which is at least a T.
This explains the somewhat convoluted signature...
Because if you dont use the "T" the collection would only accept instances of Object.
For example String is subtype of Object, but would not compile because the collection would only accept Object instances.
This is due to covariance vs. contravariance.
As a general rule:
If a generic type T is used to return values, then you use <? extends T> as in Iterator
If a generic type T is used to accept values, then you use <? super T> as in Comparable
Why this code does not compile (Parent is an interface)?
List<? extends Parent> list = ...
Parent p = factory.get(); // returns concrete implementation
list.set(0, p); // fails here: set(int, ? extends Parent) cannot be applied to (int, Parent)
It's doing that for the sake of safety. Imagine if it worked:
List<Child> childList = new ArrayList<Child>();
childList.add(new Child());
List<? extends Parent> parentList = childList;
parentList.set(0, new Parent());
Child child = childList.get(0); // No! It's not a child! Type safety is broken...
The meaning of List<? extends Parent> is "The is a list of some type which extends Parent. We don't know which type - it could be a List<Parent>, a List<Child>, or a List<GrandChild>." That makes it safe to fetch any items out of the List<T> API and convert from T to Parent, but it's not safe to call in to the List<T> API converting from Parent to T... because that conversion may be invalid.
List<? super Parent>
PECS - "Producer - Extends, Consumer - Super". Your List is a consumer of Parent objects.
Here's my understanding.
Suppose we have a generic type with 2 methods
type L<T>
T get();
void set(T);
Suppose we have a super type P, and it has sub types C1, C2 ... Cn. (for convenience we say P is a subtype of itself, and is actually one of the Ci)
Now we also got n concrete types L<C1>, L<C2> ... L<Cn>, as if we have manually written n types:
type L_Ci_
Ci get();
void set(Ci);
We didn't have to manually write them, that's the point. There are no relations among these types
L<Ci> oi = ...;
L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types.
For C++ template, that's the end of story. It's basically macro expansion - based on one "template" class, it generates many concrete classes, with no type relations among them.
For Java, there's more. We also got a type L<? extends P>, it is a super type of any L<Ci>
L<Ci> oi = ...;
L<? extends P> o = oi; // ok, assign subtype to supertype
What kind of method should exist in L<? extends P>? As a super type, any of its methods must be hornored by its subtypes. This method would work:
type L<? extends P>
P get();
because in any of its subtype L<Ci>, there's a method Ci get(), which is compatible with P get() - the overriding method has the same signature and covariant return type.
This can't work for set() though - we cannot find a type X, so that void set(X) can be overridden by void set(Ci) for any Ci. Therefore set() method doesn't exist in L<? extends P>.
Also there's a L<? super P> which goes the other way. It has set(P), but no get(). If Si is a super type of P, L<? super P> is a super type of L<Si>.
type L<? super P>
void set(P);
type L<Si>
Si get();
void set(Si);
set(Si) "overrides" set(P) not in the usual sense, but compiler can see that any valid invocation on set(P) is a valid invocation on set(Si)
This is because of "capture conversion" that happens here.
Every time the compiler will see a wildcard type - it will replace that by a "capture" (seen in compiler errors as CAP#1), thus:
List<? extends Parent> list
will become List<CAP#1> where CAP#1 <: Parent, where the notation <: means subtype of Parent (also Parent <: Parent).
java-12 compiler, when you do something like below, shows this in action:
List<? extends Parent> list = new ArrayList<>();
list.add(new Parent());
Among the error message you will see:
.....
CAP#1 extends Parent from capture of ? extends Parent
.....
When you retrieve something from list, you can only assign that to a Parent.
If, theoretically, java language would allow to declare this CAP#1, you could assign list.get(0) to that, but that is not allowed. Because CAP#1 is a subtype of Parent, assigning a virtual CAP#1, that list produces, to a Parent (the super type) is more that OK. It's like doing:
String s = "s";
CharSequence s = s; // assign to the super type
Now, why you can't do list.set(0, p)? Your list, remember, is of type CAP#1 and you are trying to add a Parent to a List<CAP#1>; that is you are trying to add super type to a List of subtypes, that can't work.
I have example:
public static <T extends Number> void doJob(List<T> pr,List<? extends Number> en,T tel){
//
System.out.println(pr.get(0).intValue());
}
List<? extends Integer> intList=new ArrayList<>();
Integer inval=200;
List<Integer> intList3=new ArrayList<Integer>(Arrays.asList(1,2,3));
doJob(intList3,intList,inval);//it is allowed
intList=intList3;
doJob(intList,intList,intList.get(0));//IT IS FORBIDDEN
Why does compiler forbid call of
doJob(intList,intList,intList.get(0)); even intList in fact is
List type?
Well that is because, Ultimately you are doing:
List<? extends Integer> ls = new ArrayList<Integer>();
doJob(ls,ls,ls.get(0));
So ls (or your intList) is actually a List of an unknown type. But what you do know, is that this unknown type extends Number.
So when you call doJob, T in doJob becomes this unknown type. Your second argument matches as List<? extends Number> is a super type to List<? extends Integer>.
As per your third argument, we already know that T is unknown and you try passing intList.get(0). Now intList.get will return ? extends Integer, i.e. another unknown type (which extends Integer). So you try passing an unknown type to a method which expects an unknown type. And two unknown's cant be guaranteed to be equal. Hence the error.
how can I write Java 1.7 code for a Java 1.6 compiler, where the diamond can not be used?
Example:
private ReplacableTree<E> convertToIntended(Tree<? extends E> from,ReplacableTree<E> to) {
TreeIterator<? extends E> it = new TreeIterator<>(from.getRoot());
while(it.hasNext()) {
E e = it.next().getElem();
to.add(e);
}
return to;
}
public class TreeIterator<E> implements TreeIter<Node<E>> {
....
}
It is not allowed to write...
TreeIterator<? extends E> it = new TreeIterator<?>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<E>(from.getRoot());
TreeIterator<? extends E> it = new TreeIterator<? extends E>(from.getRoot());
Especially the third one is confusing for me. Why doesn't it work? I just want to read Elements from a Tree (which could be a subtype tree), and when puch each of it in a new Tree with Elements of type E.
Wildcard types are not permitted as type arguments in class instance creation expressions:
It is a compile-time error if any of the type arguments used in a class instance creation expression are wildcard type arguments (ยง4.5.1).
so the first and third variants are not valid.
Variant 2 is invalid because the TreeIterator<E> constructor wants a Node<E>, but you give it a Node<? extends E>.
As for the solution, Java 5 and 6 did not have type inference for constructors, but do have type inference for methods, and in particular capture conversion. The following ought to compile:
TreeIterator<? extends E> it = makeIterator(from.getRoot());
where
private <E> TreeIterator<E> makeIterator(Node<E> node) {
return new TreeIterator<E>(node);
}
Edit: You asked in the comment:
The contstructor parameter type for TreeIterator is Node<E>. The constructor parameter of Node<E> therefore is E. When writing variant two, eclipse says the following: The constructor TreeIterator<E>(Node<capture#2-of ? extends E> ) is undefined What does that mean?
Being a wildcard type, the type Node<? extends E> represents a family of types. Node<capture#2-of ? extends E> refers to a specific type in that family. That distinction is irrelevant in this case. What matters is that Node<? extends E> is not a subtype of Node<E>, and hence you can't pass an instance of Node<? extends E> to a constructor expecting a Node<E>.
In short you don't write Java 7 code for a Java 6 compiler - you have to use the old, duplicative non-diamond syntax. And no, you can't specify a target of 1.6 with source 1.7, it won't work!
meriton already explained it well. I just want to suggest that you could as well do it without the wildcard declaration:
TreeIterator<E> it = new TreeIterator<E>(from.getRoot());
Usually, <> means to just use the same type parameter as in the declaration to the left. But in this case, that declaration is a wildcard.
It doesn't make sense to make a constructor with a wildcard type parameter, new TreeIterator<? extends E>(...) because, usually, if you don't care what parameter to use, you should just pick any type that satisfies that bound; which could be E, or any subtype thereof.
However, in this case, that doesn't work because the constructor of TreeIterator<E> takes an object with the type parameter <E>. You didn't show the source code of TreeIterator, so I can't see what it does, but chances are that its bound is too strict. It could probably be refactored to make the type parameter <? extends E>.
But there are some cases where that is not possible. In such a case, you can still eliminate the need for the type parameter E through a "capture helper" (what meriton suggests above) to turn something which takes parameter E into something that takes a wildcard ? extends E.
I know this is an old question, but in case someone stumbles on this, I would have thought the most obvious way of writing it would have been:
private <U extends E> ReplaceableTree<E> convertToIntended(Tree<U> from, ReplaceableTree<E> to)
{
TreeIterator<U> it = new TreeIterator<U>(from.getRoot());
while(it.hasNext())
{
E e = it.next().getElem();
to.add(e);
}
return to;
}
I don't think such a change would break existing code as the type constraints are the same from the existing signature to this one.