Consider the following code sample:
@SafeVarargs
public static <U> Object[] sortedCopy(Comparator<? super U> comparator, U... values) {
U[] copy = Arrays.copyOf(values, values.length);
Arrays.sort(copy, comparator);
return copy; //copy is implicitly cast to Object[] -> no heap pollution
}
public static <U> Object[] sortedCopy(U... values) {
return sortedCopy(Comparator.naturalOrder(), values); //why does this compile???
}
I would have expected the compiler to reject the line in sortedCopy(U...), with the following reasoning:
The return type of Comparator.naturalOrder() is Comparator<T>, where T is a generic type parameter of this method that must fulfill the constraint T extends Comparable<? super T>. Since the method does not accept any arguments, and its invocation in the above code does not explicitly specify a type for T (like Comparator.<U>naturalOrder(), which would not compile because U does not extend Comparable), T has to be inferred another way. The only way I can see to infer T is by the method signature of sortedCopy(Comparator<? super U>, U...). The compiler knows the type of values and can thus infer U, and in turn can also infer bounds for T, namely the bounded wildcard ? super U. But then, the compiler should realize that any ? super U can never meet the requirement for T specified by Comparator.naturalOrder(), which is T extends Comparable<? super T>, because U itself does not extend Comparable<? super U>, so any supertype of U cannot either.
What confuses me is that the compiler does generate an error when I change the signature from sortedCopy(U...) to sortedCopy(U[]). I suppose this has to do with the fact that, in the second case, U is present at runtime as the type of the array, whereas in the first case, it is not. But I still don't understand why that would make the method call in question valid, because:
- As I understand it, a varargs parameter of a generic type is converted to an
Object[]if the values passed to the method as vararg arguments are of a generic and therefore non-reifiable type, which, if I understand correctly, is the case in the above code becauseUfromsortedCopy(U...)is non-reifiable. But even then, why does the compiler not realize thatObjectdoes not extendComparable<? super Object>? - The previous argument talks about runtime types. However, we're still pre-compilation, so speculations about runtime-types should not even be relevant in this context, because, while
Umight not be present anymore at runtime, the compiler still knows it and should be able to check whether the equality constraints are fulfilled, regardless of whether the method parameter is an array or a vararg.
So why does the line in question from the above code sample still compile?
Beside that, I would also be grateful for feedback in case the @SafeVarargs annotation of the method sortedCopy(Comparator<? super U>, U...) is inappropriate. I believe it is, but I'm not confident about it.