I have a Problem with a generic method after upgrading to Java 1.8, which was fine with Java 1.6 and 1.7 Consider the following code:
public class ExtraSortList<E> extends ArrayList<E> {
ExtraSortList(E... elements) {
super(Arrays.asList(elements));
}
public List<E> sortedCopy(Comparator<? super E> c) {
List<E> sorted = new ArrayList<E>(this);
Collections.sort(sorted, c);
return sorted;
}
public static void main(String[] args) {
ExtraSortList<String> stringList = new ExtraSortList<>("foo", "bar");
Comparator<? super String> compGen = null;
String firstGen = stringList.sortedCopy(compGen).get(0); // works fine
Comparator compRaw = null;
String firstRaw = stringList.sortedCopy(compRaw).get(0); // compiler ERROR: Type mismatch: cannot convert from Object to String
}
}
I tried this with the Oracle javac (1.8.0_92) and with Eclipse JDT (4.6.1) compiler. It is the same result for both. (the error message is a bit different, but essentially the same)
Beside the fact, that it is possible to prevent the error by avoiding raw types, it puzzles me, because i don't understand the reason.
Why does the raw method parameter of the sortedCopy-Method have any effect on the generic type of the return value? The generic type is already defined at class level. The method does not define a seperate generic type. The reference list
is typed to <String>
, so should the returned List.
Why does Java 8 discard the generic type from the class on the return value?
EDIT: If the method signature of sortedCopy is changed (as pointed out by biziclop) to
public List<E> sortedCopy(Comparator c) {
then the compiler does consider the generic type E
from the type ExtraSortList<E>
and no error appears. But now the parameter c
is a raw type and thus the compiler cannot validate the generic type of the provided Comparator.
EDIT: I did some review of the Java Language Specification and now i think about, whether i have a lack of understanding or this is a flaw in the compiler. Because:
- Scope of a Declaration of the generic type
E
is the classExtraSortList
, this includes the methodsortedCopy
. - The method
sortedCopy
itself does not declare a generic type variable, it just refers to the type variableE
from the class scope. see Generic Methods in the JLS - The JLS also states in the same section
Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (§18 (Type Inference)).
- The reference
stringList
is defined withString
, thus the compiler does not need to infer a type forE
on the invocation ofsortedCopy
because it is already defined. - Because
stringList
already has a reified type forE
, the parameterc
should beComparator<? super String>
for the given invocation. - The return type should also use the already reified type
E
, thus it should beList<String>
.
This is my current understanding of how i think the Java compiler should evaluate the invocation. If i am wrong, an explanation why my assumptions are wrong would be nice.