Java Generics: unbounded wildcard generalization not working past second level [duplicate] - java

This question already has answers here:
Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
(3 answers)
Closed 8 years ago.
I guess this is a very confusing title, but I don't know what else to call - probably answered somewhere as well, but I couldn't find anything. Take this example:
List<Class<? extends Integer>> myList;
void foo() {
bar(myList);
}
void bar(List<Class<?>> a) { /* ... */ }
It doesn't compile (not applicable arguments, it says). If I remove the bounding it works fine:
List<Class<?>> myList;
void foo() {
bar(myList);
}
void bar(List<Class<?>> a) { /* ... */ }
Class<? extends Integer> is just more specific than Class<?>. How come it stops working? Note that this problem is only in the second level generics. If there wasn't a List, just Class<? extends Integer> and Class<?>, it works as well. But it seems to stop working when the generics is two ore more levels deep. Any reasons/workarounds/etc?

Note that List<B> is not a subtype of List<A> even if B is a subtype of A. Therefore, List<Class<? extends Integer>> is not a subtype of List<Class<?>> even though you can assign a Class<? extends Integer> object to a Class<?> variable. Just consider that in bar, it would be legal to invoke a.add(Object.class), as Class<Object> is a subtype of Class<?>.
You therefore want to extend the bar argument type to List<? extends Class<?>>.

Related

Why is Class<T extends Base> not applicable to Class<Base>? [duplicate]

This question already has answers here:
Is List<Dog> a subclass of List<Animal>? Why are Java generics not implicitly polymorphic?
(19 answers)
Closed 4 years ago.
I am in the creation of a small application and I stumbled over the following problem.
There is a List<Class<MyCustomBaseClass>> in my application and a function with the signature public <T extends MyCustomBaseClass> void addClass(Class<T> clazz).
The AddClass should put the clazz into the List. But I get the following error there:
The method add(Class<MyCustomBaseClass>) in the type List<Class<MyCustomBaseClass>> is not applicable for the arguments (Class<T>)
Here are my 3 classes as simplified as I could make them:
// Program.java
package me.mischa.stackoverflow;
import java.util.ArrayList;
import java.util.List;
public class Program {
private List<Class<MyCustomBaseClass>> _listOfClasses;
private static Program _instance;
public Program() {
_listOfClasses = new ArrayList<>();
}
public static void main(String[] args) {
Program program = new Program();
program.addClass(MyCustomChildClass.class);
}
public <T extends MyCustomBaseClass> void addClass(Class<T> clazz) {
_listOfClasses.add(clazz);
}
}
.
// MyCustomBaseClass.java
package me.mischa.stackoverflow;
public class MyCustomBaseClass {
}
.
// MyCustomChildClass.java
package me.mischa.stackoverflow;
public class MyCustomChildClass extends MyCustomBaseClass {
}
The error is at the line _listOfClasses.add(clazz);
I do not understand why <T extends MyCustomBaseClass> should not be compatible with <MyCustomBaseClass>
Java's generics are invariant. That means that, as a type parameter, Class<MyCustomBaseClass> means exactly that, no Class object representing a subclass of MyCustomBaseClass is allowed.
In your addClass method, you've only given an upper bound on T when defining it -- T could be a subclass of MyCustomBaseClass, e.g. your class MyCustomChildClass. The compiler disallows this call because of the mismatch.
You can widen what's allowed in _listOfClasses by providing a matching upper bound, which will allow the method addClass to compile.
private List<Class<? extends MyCustomBaseClass>> _listOfClasses;
Incidentally, because it doesn't really matter exactly what type T is in addClass, you can remove it and use a wildcard.
public void addClass(Class<? extends MyCustomBaseClass> clazz) {
Java's generics are invariant. There's a reason for that. Imagine the following code (NOTE: Number is a supertype of both Integer and Double):
List<Double> doublesOnly = new ArrayList<Double>();
List<Number> numbers = doublesOnly;
numbers.add(new Integer(5));
Double d = doublesOnly.get(0);
In the above, if it had been valid java, you are assigning an Integer to a Double, which is a problem because an Integer isn't a Double. This is why in actual fact, if you attempt to compile the above, it won't work; the second line is marked as invalid java, because a List<Double> cannot be assigned to a List<Number>. There is a solution:
List<Double> doublesOnly = new ArrayList<Double>();
List<? extends Number> numbers = doublesOnly;
numbers.add(new Integer(5));
Double d = doublesOnly.get(0);
This time, line 3 is the error: You cannot add anything to a List<? extends Something>, other than null. There's no way to fix this code and that's good because we're doing something fundamentally bad.
The solution in your specific case is two-fold:
More generally you should avoid the notion of using Class<?> in your APIs. Generally, use factories instead.
If you must, try something like: List<Class<? extends MyCustomBaseClass>>. Yes, 2 extends. You can add a Class<MyCustomChildClass> to this list.

Java Wildcard capture [duplicate]

This question already has an answer here:
Capturing wildcards in Java generics
(1 answer)
Closed 6 years ago.
<T extends Number> void m1(List<T> list) {
list.add(list.get(0));
}
void m2(List<? extends Number> list) {
list.add(list.get(0));
}
I found difficult to understand the difference between above two methods.
First method m1() compiles successfully, but method m2() produces a compiler error:
The method add(capture#1-of ? extends Number) in the type List<capture#1-of ? extends Number> is not applicable for the arguments (capture#2-of ? extends Number)
Because you cant add an iten on a list of type with upper bounds! You could have a List or List, where one doesnt fit in the other for modifications operations!
List<? extends Number> list = new ArrayList<Integer>();
List<? extends Number> list = new ArrayList<Double>();
List<? extends Number> list = new ArrayList<Long>();
In this case, the variable list could have any type in instance that extends Number. So you can pass it in your method, for example. But there, you don't really know which the type could be. You could have a ArrayList<Integer> and saying to it add a new Double. In compile time makes sense, because Double extends Number, but in runtime the list could no be of this type and throw an Exception.
Just remember that, when you use the upper bounds, <? extends T>, you can't modify the list, just read it! There is the Oracle tutorial (see Wildcards chapter contents - Upper Bounded Wildcards, Lower Bounded Wildcards etc.)

GenericWildcard is not applicable [duplicate]

This question already has answers here:
multiple nested wildcard - arguments not applicable [duplicate]
(2 answers)
Can't cast to to unspecific nested type with generics
(5 answers)
Closed 8 years ago.
I have a function
public static void bar (final List<List<?>> list)
{
}
which I can call with a wildcard (<?>)
bar(new ArrayList<List<?>>());
but not with another type (e.g. String)
// The method bar(List<List<?>>) in the type Foo is not
// applicable for the arguments (ArrayList<List<String>>)
bar(new ArrayList<List<String>>());
However this works for the similar function
public static void foo(List<?> l)
{
}
public static void main(String[] args)
{
// no error
foo(new ArrayList<String>());
}
Can you please explain, why the compiler complains in the first case but not in the second?
You should declare your method as:
private void bar(final List<? extends List<?>> lists) {...}
In this case the call bar(new ArrayList<List<String>>()); would work.
The explanation
In short:
List<SomeType> - The compiler will expect a call with exactly the same type.
List<? extends SomeType> - The compiler will expect a call with a class that is a compatible (sublass) with SomeType.
In your case a definition
void bar (final List<List<?>> list)
will expect a parameter whose definition is exactly List<List<?>>() nestedList;
On the other hand, when you specify your method as:
void bar(final List<? extends List<?>> lists)
Then you're saying that you have a list whose types are upper-bounded by List<?>, so ArrayList<String> would be a valid candidate for the nested list
From Oracle docs:
There is a small but very important difference here: we have replaced
the type List with List. Now drawAll() will
accept lists of any subclass of Shape, so we can now call it on a
List if we want.
List is an example of a bounded wildcard. The ?
stands for an unknown type, just like the wildcards we saw earlier.
However, in this case, we know that this unknown type is in fact a
subtype of Shape. (Note: It could be Shape itself, or some subclass;
it need not literally extend Shape.) We say that Shape is the upper
bound of the wildcard.

Generic method with parameters vs. non-generic method with wildcards

According to this entry in the Java Generics FAQ, there are some circumstances where a generic method has no equivalent non-generic method that uses wildcard types. According to that answer,
If a method signature uses multi-level wildcard types then there is always a difference between the generic method signature and the wildcard version of it.
They give the example of a method <T> void print1( List <Box<T>> list), which "requires a list of boxes of the same type." The wildcard version, void print2( List <Box<?>> list), "accepts a heterogenous list of boxes of different types," and thus is not equivalent.
How do you interpret the the differences between the following two method signatures:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
Intuitively, it seems like these definitions should be equivalent. However, the call f(ArrayList.class) compiles using the first method, but the call g(ArrayList.class) using the second method results in a compile-time error:
g(java.lang.Class<? extends java.lang.Iterable<?>>) in Test
cannot be applied to (java.lang.Class<java.util.ArrayList>)
Interestingly, both functions can be called with each others' arguments, because the following compiles:
class Test {
<T extends Iterable<?>> void f(Class<T> x) {
g(x);
}
void g(Class<? extends Iterable<?>> x) {
f(x);
}
}
Using javap -verbose Test, I can see that f() has the generic signature
<T::Ljava/lang/Iterable<*>;>(Ljava/lang/Class<TT;>;)V;
and g() has the generic signature
(Ljava/lang/Class<+Ljava/lang/Iterable<*>;>;)V;
What explains this behavior? How should I interpret the differences between these methods' signatures?
Well, going by the spec, neither invocation is legal. But why does the first one type check while the second does not?
The difference is in how the methods are checked for applicability (see §15.12.2 and §15.12.2.2 in particular).
For simple, non-generic g to be applicable, the argument Class<ArrayList> would need to be a subtype of Class<? extends Iterable<?>>. That means ? extends Iterable<?> needs to contain ArrayList, written ArrayList <= ? extends Iterable<?>. Rules 4 and 1 can be applied transitively, so that ArrayList needs to be a subtype of Iterable<?>.
Going by §4.10.2 any parameterization C<...> is a (direct) subtype of the raw type C. So ArrayList<?> is a subtype of ArrayList, but not the other way around. Transitively, ArrayList is not a subtype of Iterable<?>.
Thus g is not applicable.
f is generic, for simplicity let us assume the type argument ArrayList is explicitly specified. To test f for applicability, Class<ArrayList> needs to be a subtype of Class<T> [T=ArrayList] = Class<ArrayList>. Since subtyping is reflexisve, that is true.
Also for f to be applicable, the type argument needs to be within its bounds. It is not because, as we've shown above, ArrayList is not a subtype of Iterable<?>.
So why does it compile anyways?
It's a bug. Following a bug report and subsequent fix the JDT compiler explicitly rules out the first case (type argument containment). The second case is still happily ignored, because the JDT considers ArrayList to be a subtype of Iterable<?> (TypeBinding.isCompatibleWith(TypeBinding)).
I don't know why javac behaves the same, but I assume for similar reasons. You will notice that javac does not issue an unchecked warning when assigning a raw ArrayList to an Iterable<?> either.
If the type parameter were a wildcard-parameterized type, then the problem does not occur:
Class<ArrayList<?>> foo = null;
f(foo);
g(foo);
I think this is almost certainly a weird case arising out of the fact that the type of the class literal is Class<ArrayList>, and so the type parameter in this case (ArrayList) is a raw type, and the subtyping relationship between raw ArrayList and wildcard-parameterized ArrayList<?> is complicated.
I haven't read the language specification closely, so I'm not exactly sure why the subtyping works in the explicit type parameter case but not in the wildcard case. It could also very well be a bug.
Guess: The thing representing the first ? (ArrayList) does not 'implement' ArrayList<E> (by virtue of the double nested wildcard). I know this sounds funny but....
Consider (for the original listing):
void g(Class<? extends Iterable<Object> x) {} // Fail
void g(Class<? extends Iterable<?> x) {} // Fail
void g(Class<? extends Iterable x) {} // OK
And
// Compiles
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList<Integer>> d = new ArrayList<ArrayList<Integer>>();
f(d);
g(d);
}
}
This
// Does not compile on g(d)
public class Test{
<T extends Iterable<?>> void f(ArrayList<T> x) {}
void g(ArrayList<? extends Iterable<?>> x) {}
void d(){
ArrayList<ArrayList> d = new ArrayList<ArrayList>();
f(d);
g(d);
}
}
These are not quite the same:
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
The difference is that g accepts a "Class of unknown that implements Iterable of unknown", but ArrayList<T> is constrained implementing Iterable<T>, not Iterable<?>, so it doesn't match.
To make it clearer, g will accept Foo implements Iterable<?>, but not AraryList<T> implements Iterable<T>.

Can all wildcards in Java be replaced by non-wildcard types?

I can't find any example where wildcards can't be replaced by a generic.
For example:
public void dummy(List<? extends MyObject> list);
is equivalent to
public <T> void dummy(List<T extends MyObject> list);
or
public <T> List<? extends T> dummy2(List<? extends T> list);
is equivalent to
public <T, U> List<U extends T> dummy(List<U extends T> list);
So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?
Nope, it is not always replaceable.
List<? extends Reader> foo();
is not equivalent to
<T> List<T extends Reader> foo();
because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.
The demonstration of using wildcards can be found in this (my) answer.
An easy answer is that, deeper level wildcards can't be replaced by a type variable
void foo( List<List<?>> arg )
is very different from
<T>
void foo( List<List<T>> arg)
This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by #josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.
Another rather random case where wildcard can't be replaced type variable:
class Base
List<? extends Number> foo()
class Derived extends Base
List<Integer> foo()
Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.
There is a usage difference for your examples.
public <T> List<? extends T> dummy2(List<? extends T> list);
returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.
Example T = Number
List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));
So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.
Example:
//Use to remove unliked numbers, thanks to the wildcard
//it is impossible to add a Number
#Override public void removeNumberCallback(List<? extends Number> list){
list.remove(13);
}

Categories

Resources