Why can I use super
only with wildcards and not with type parameters?
For example, in the Collection
interface, why is the toArray
method not written like this
interface Collection<T>{
<S super T> S[] toArray(S[] a);
}
super
to bound a named type parameter (e.g. <S super T>
) as opposed to a wildcard (e.g. <? super T>
) is ILLEGAL simply because even if it's allowed, it wouldn't do what you'd hoped it would do, because since Object
is the ultimate super
of all reference types, and everything is an Object
, in effect there is no bound.
In your specific example, since any array of reference type is an Object[]
(by Java array covariance), it can therefore be used as an argument to <S super T> S[] toArray(S[] a)
(if such bound is legal) at compile-time, and it wouldn't prevent ArrayStoreException
at run-time.
What you're trying to propose is that given:
List<Integer> integerList;
and given this hypothetical super
bound on toArray
:
<S super T> S[] toArray(S[] a) // hypothetical! currently illegal in Java
the compiler should only allow the following to compile:
integerList.toArray(new Integer[0]) // works fine!
integerList.toArray(new Number[0]) // works fine!
integerList.toArray(new Object[0]) // works fine!
and no other array type arguments (since Integer
only has those 3 types as super
). That is, you're trying to prevent this from compiling:
integerList.toArray(new String[0]) // trying to prevent this from compiling
because, by your argument, String
is not a super
of Integer
. However, Object
is a super
of Integer
, and a String[]
is an Object[]
, so the compiler still would let the above compile, even if hypothetically you can do <S super T>
!
So the following would still compile (just as the way they are right now), and ArrayStoreException
at run-time could not be prevented by any compile-time checking using generic type bounds:
integerList.toArray(new String[0]) // compiles fine!
// throws ArrayStoreException at run-time
Generics and arrays don't mix, and this is one of the many places where it shows.
Again, let's say that you have this generic method declaration:
<T super Integer> void add(T number) // hypothetical! currently illegal in Java
And you have these variable declarations:
Integer anInteger
Number aNumber
Object anObject
String aString
Your intention with <T super Integer>
(if it's legal) is that it should allow add(anInteger)
, and add(aNumber)
, and of course add(anObject)
, but NOT add(aString)
. Well, String
is an Object
, so add(aString)
would still compile anyway.
On generics typing rules:
List<Animal> animals = new ArrayList<Dog>()
?List
is different from List<Object>
which is different from a List<?>
On using super
and extends
:
Java Generics: What is PECS?
extends
consumer super
"super
and extends
in Java Generics<E extends Number>
and <Number>
?List<? extends Number>
data structures? (YOU CAN'T!)