Reputation: 383
I am unable to understand why second invocation below gives compiler error. Why is it not able to infer Number as type argument as in the first invocation?
typeArgInference(new Integer[100], new ArrayList<Number>()); // Infers Number
typeArgInference(new Number[100], new ArrayList<Integer>()); // compiler error
<T> void typeArgInference(T[] a, Collection<T> c) {}
May be I am missing something here. If there is any rule in JLS on this behavior, please do include the link.
Upvotes: 0
Views: 76
Reputation: 140319
Generics are invariant, arrays are covariant:
Integer[]
can act as a Number[]
(because Integer
is a subclass of Number
)ArrayList<Integer>
can't act as an ArrayList<Number>
(because you can add e.g. a Double
to the latter, but you mustn't add a Double
into a list of Integer
s).Of course, you can try to put a Double
into a Number[]
, but it that would fail with ArrayStoreException
if your Number[]
is really an Integer[]
. This would be a runtime failure.
Making arrays covariant was a fudge to have sort-of generic sort-of collections before the language supported generics. It was realized that this was a problem (because runtime failures suck), hence why generics were designed to be invariant.
Since you asked for JLS links:
Both of your examples would compile if you added an upper bound to the list type:
<T> void typeArgInference(T[] a, Collection<? extends T> c) {}
In both cases, Number
is a type that would satisfy the type constraints:
Integer[]
can act as a Number[]
; and ArrayList<Number>
is a Collection<Number>
, which is a Collection<? extends Number>
.Number[]
can act as a Number[]
(obviously); and ArrayList<Integer>
is a Collection<Integer>
, which is a Collection<? extends Number>
.You just then could not add anything into the Collection
within that method (other than literal null
; or by breaking type safety via raw types).
Upvotes: 7