Note: This is a spin-off from Comparable and Comparator contract with regards to null
This code compiles and runs fine in Eclipse (20090920-1017
)
import java.util.*;
public class SortNull {
static <T extends Comparable<? super T>>
Comparator<T> nullComparableComparator() {
return new Comparator<T>() {
@Override public int compare(T el1, T el2) {
return
el1 == null ? -1 :
el2 == null ? +1 :
el1.compareTo(el2);
}
};
}
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<Integer>(
Arrays.asList(3, 2, 1, null, null, 0)
);
Comparator<Integer> numbersComp = nullComparableComparator();
Collections.sort(numbers, numbersComp);
System.out.println(numbers);
// "[null, null, 0, 1, 2, 3]"
List<String> names = new ArrayList<String>(
Arrays.asList("Bob", null, "Alice", "Carol")
);
Comparator<String> namesComp = nullComparableComparator();
Collections.sort(names, namesComp);
System.out.println(names);
// "[null, Alice, Bob, Carol]"
}
}
And yet it doesn't compile on javac 1.6.0_17
. This is the error message:
SortNull.java:17: incompatible types; no instance(s) of type variable(s) T exist
so that java.util.Comparator<T> conforms
to java.util.Comparator<java.lang.Integer>
found : <T>java.util.Comparator<T>
required: java.util.Comparator<java.lang.Integer>
Comparator<Integer> numbersComp = nullComparableComparator();
SortNull.java:25: incompatible types; no instance(s) of type variable(s) T exist
so that java.util.Comparator<T> conforms
to java.util.Comparator<java.lang.String>
found : <T>java.util.Comparator<T>
required: java.util.Comparator<java.lang.String>
Comparator<String> namesComp = nullComparableComparator();
2 errors
Can someone explain why the discrepancy? Is this a bug? If so, who has the bug?
Eclipse Java compiler is an incremental Java builder The Java compiler built in Eclipse is a part of JDT Core component (JDT: Java Development Tool). An incremental compiler automatically compiles code when changes are detected.
Generics are checked at compile-time for type-correctness. The generic type information is then removed in a process called type erasure. For example, List<Integer> will be converted to the non-generic type List , which ordinarily contains arbitrary objects.
Generics provide strong compile-time type checking and reduces risk of ClassCastException and explicit casting of objects.
Generics in Java are implemented using a type erasure mechanism. The compiler translates all type parameters in the source code to their bounding type in the class file. A type parameter's bounding type is Object if a bound type is not specified.
This is a confirmed bug: Bug ID 6468354. Here's an extract of relevance:
This problem is caused by the fact that sometimes javac's implementation of JLS3 15.12.2.8 ignores recursive bounds, sometimes not (as in this case). When recursive bounds contains wildcards, such bounds are included when computing uninferred type variables. This makes subsequent subtyping
test (Integer <: Comparable<? super T>
whereT
is a type-variable to be inferred).Will be fixed after 6369605
Occured to me on WinXP with 1.6.0_13 as well. Ah well, I'll just stick using Eclipse :)
You can get around this by explicitly specifying the generic class:
Comparator<String> namesComp = Stack.<String>nullComparableComparator();
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