I'm having trouble with this:
public class Test {
static class TestType {}
static class Pair<A,B>{}
public static void main( String [] args ) throws Exception {
Collection<? extends TestType> val = null;
List<? extends TestType> single =
testSingle( val ); // OK
Pair<String,List<? extends TestType>> pair =
testPair( val ); // ERROR
}
static <T extends TestType> List<T> testSingle( Collection<T> val ){
return null;
}
static <T extends TestType> Pair<String,List<T>> testPair( Collection<T> val ){
return null;
}
}
Why is the first one working and the second one isn't?
The Error message ist:
Type mismatch: cannot convert from Test.Pair<String,List<capture#3-of ? extends Test.TestType>> to Test.Pair<String,List<? extends Test.TestType>>
EDIT:
using Braj's Answer I setup this to clearify the Problem when not using generic type T:
public class Test {
static class TestType {}
static class Implementation extends TestType {}
static class Pair<A,B>{}
public static void main( String [] args ) throws Exception {
Collection<Implementation> val = null;
List<Implementation> sinle = testSingle( val ); // OK
Pair<String,List<Implementation>> pair = testPair( val ); // OK
Pair<String,List<Implementation>> pair2 = testPair2( val ); // ERROR
Pair<String,List<Implementation>> pair3 = testPair3( val ); // ERROR
Pair<String,? extends List<Implementation>> pair4 = testPair4( val ); // ERROR
run( val );
}
private static void run( Collection<? extends TestType> val ){
List<? extends TestType> single = testSingle( val ); // OK
Pair<String,List<? extends TestType>> pair = testPair( val ); // ERROR
Pair<String,List<? extends TestType>> pair2 = testPair2( val ); // ERROR
Pair<String,List<? extends TestType>> pair3 = testPair3( val ); // OK
Pair<String,? extends List<? extends TestType>> pair4 = testPair4( val ); // OK
Pair<String,? extends List<? extends TestType>> pairX = testPair( val ); // OK
//My-Name-Is
@SuppressWarnings( "unchecked" )
Pair<String,List<? extends TestType>> fixed =
(Pair<String,List<? extends TestType>>)
(Pair<String,?>) testPair( val ); // OK but ugly and stupid(?)
}
private static <T extends TestType> List<T> testSingle( Collection<T> val ){
return null;
}
private static <T extends TestType> Pair<String,List<T>> testPair( Collection<T> val ){
return null;
}
// Braj1
private static <T extends TestType> Pair<String, List<TestType>> testPair2(Collection<T> val) {
return null;
}
// Braj2
private static <T extends TestType> Pair<String, List<? extends TestType>> testPair3(Collection<T> val) {
return null;
}
// Seelenvirtuose
private static <T extends TestType> Pair<String, ? extends List<? extends TestType>> testPair4(Collection<T> val) {
return null;
}
// This one works in the way I wanted.
private static <T extends TestType> void runOK( Collection<T> val ){
List<T> single = testSingle( val ); // OK
Pair<String,List<T>> pair = testPair( val ); // OK
}
}
EDIT2: I can fix this in run() using:
@SuppressWarnings( "unchecked" )
Pair<String,List<? extends TestType>> fixed =
(Pair<String,List<? extends TestType>>)
(Pair<String,?>) testPair( val );
But this is rather ugly and stupid (?).
EDIT3: I edited the above to include Seelenviruose's answer and it's still getting weirder.
I still don't know why this is needed...
EDIT4: Finally got it working without ugly casts: if I use <T extends TestType> run(...) the compiler doesn't complain. I changed it above.
The return type of your second method call is
Pair<String,List<? extends TestType>>
Why is that the return type? Well, the compiler infers that type from the input argument val that is of type Collection<? extends TestType>.
The upper bounded wildcards are the Java mechanism to break generic's inherent invariance. With such a construct the following is possible:
Collection<? extends Number> c1 = new LinkedList<Integer>();
Collection<? extends Number> c2 = new HashSet<Double>();
Note, that I introduced two kinds of polymorphism. One for the collection type, and another for the type variable.
Without the upper bounded wildcard the following construct is be a compiler error:
Collection<Number> c3 = new LinkedList<Integer>(); // compiler error
Why the error? Simple: If you have a Collection<Number> you would expect to being able to add a Double object into it. But this would not be allowed into a LinkedList<Integer>. Hence, error.
The downside of this upper bounded wildcard is now, that you can't add anything (except null) into a Collection<? extends Number>.
Cut: If you understood it until now, the rest should be easy.
You now introduced an additional layer into the generic return type. You do not return a list of something, you return a pair of a string and some list.
So the upper bound now must not be on the type T of your list elements. It must be placed onto the list itself!
With the following method declaration
<T extends TestType> Pair<String, ? extends List<T>> testPair(Collection<T> val) { ... }
the following call will be allowed:
Pair<String, ? extends List<? extends TestType>> pair = testPair(val);
But ... I doubt that anything reasonable is possible with such a pair. If so, you must reconsider your design.
Why is the first one working and the second one isn't?
Because List<ASpecificSubTypeOfTestType> (the return of the first method) is a subtype of List<? extends TestType>, but Pair<String, List<ASpecificSubTypeOfTestType>> (the return of the second method) is not a subtype of Pair<String, List<? extends TestType>>.
Let us change your example from Pair to List, and TestType to Object for a moment:
public class Test {
public static void main( String [] args ) throws Exception {
Collection<?> val = null;
List<?> single = testSingle( val ); // OK
List<List<?>> pair = testList( val ); // ERROR
}
static <T> List<T> testSingle( Collection<T> val ){
return null;
}
static <T> List<List<T>> testList( Collection<T> val ){
return null;
}
}
From a technical point of view, List<SomeSpecificType> is a subtype of List<?>, but List<List<SomeSpecificType>> is not a subtype of List<List<?>>, for the same reason that List<String> is not a subtype of List<Object> -- the type parameters are different concrete types (one is List<SomeSpecificType> and the other is List<?>).
For a more practical reasoning, testList returns a List<List<T>> for some T. We don't know what that T is, but we know that it is some concrete type. And this List<List<T>> that is returned is a list that can only contain List<T>. It cannot contain List<S> if S is not T, because List<S> and List<T> are not subtypes of each other. Even though we don't know what T is, we know that there exists some choice of T, and all the element lists must have that T as the type parameter.
On the other hand, the type you're assigning it to, List<List<?>>, is a list that can contain every type of list at the same time. So you can put in a List<Integer> and a List<String>, etc. at the same time and it would be okay. You can never do that with a List<List<T>>, no matter what you choose T to be.
Therefore, the two types are clearly incompatible.
What can go wrong? With the List<List<?>> reference, you can insert List<Integer> and List<String> into the list. And then with the List<List<T>> reference inside the function, you can extract all the elements as List<T>, which they won't be. So it is unsafe.
You might say, what if I never put things into the List<List<?>> reference? Is that safe? But if that were the case, you should instead use List<? extends List<?>>. The ? extends wildcard makes it a consumer, so you cannot insert anything into it except null; and it also makes the type compatible (List<List<SomeSpecificType>> is a subtype of List<? extends List<?>>).
The moral of the story is, wildcards in deeper parameters do not mean what you think they do.
Try
private static <T extends TestType> Pair<String, List<TestType>> testPair(Collection<T> val) {...}
Pair<String, List<TestType>> pair = testPair(val);
OR
private static <T extends TestType> Pair<String, List<? extends TestType>> testPair(Collection<T> val) {...}
Pair<String, List<? extends TestType>> pair = testPair(val);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With