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