views:

194

answers:

2

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);
}
+1  A: 

Because you are specifying the lower bound of the inheritance hierarchy.

In that way you can provide this list:

List<? super Cat>

with both Cat, Animal and Object, if Animal is a super-class of Cat.

Lars Andren
This is not what's being asked. OP asks why you can do `<? super T>` but NOT `<S super T>`. The answer is because even if it's allowed, it just doesn't work the way OP hoped it would.
polygenelubricants
+4  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.


A non-array example

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.


See also

Related questions

On generics typing rules:

On using super and extends:

polygenelubricants
This way Collection"<? super Integer> should take every thing. Because every thing is Object and Object is super type of Integer.
mohsenof
@mohsenof: even though `String` is an `Object`, a `Collection<String>` is not a `Collection<? super Integer>`, because generics are invariant: a `Collection<String>` is NOT a `Collection<Object>`. A `String[]` however is an `Object[]`, because arrays are covariant. Again, generics and arrays don't mix, and they run under very different type rules. Read _Effective Java 2nd Edition, Item 25: Prefer lists to arrays_.
polygenelubricants
so why would <S super T> List<S> addToList(List<S> list, T element){ list.add(element); return list; }not make sense?
ILMTitan