(If this is a duplicate please point me to the right answer! I searched and read several (>5) related questions but none seemed on the mark. Also looked at the Generics FAQ and other sources...)
It is apparently proper practice that when a collection class takes a comparator it should have the type Comparator<? super T>
for your parameterized type T
. And you can see that lots of places, e.g., TreeMap
. Okay.
My problem is working with Comparator.naturalOrder()
which is parameterized on T extends Comparable<? super T>
but returns a Comparator<T>
. I'm trying to have a field in my collection class that holds either the user-specified comparator or the Comparator.naturalOrder
comparator.
I can't get it to work. My questions, all related, are:
Comparator.naturalOrder
properly used?
naturalOrder
comparator?T
not T implements Comparable<? super T>
, so that's the design pattern chosen, how is naturalOrder
useful since it requires the latter bounded wildcard, not the unconstrained type parameter?Thanks!
Here follows the actual examples with compiler errors:
So: If I have code like this in some class where T has no bounds (as in all existing collection classes):
class Foo<T> {
private Comparator<? super T> comparator;
public void someMethod(Comparator<? super T> comparator)
{
this.comparator = comparator; // no compile error
this.comparator = Comparator.naturalOrder(); // incompatible types
}
}
with this error:
Error:(331, 50) java: incompatible types: inferred type does not conform to upper bound(s)
inferred: T
upper bound(s): java.lang.Comparable<? super T>
So if I decide to forgo the advantages of ? super T
then I have:
class Foo<T> {
private Comparator<T> comparator;
public void someMethod(ComparatorT> comparator)
{
this.comparator = comparator; // no compile error
this.comparator = Comparator.naturalOrder(); // incompatible types
}
}
where I have
Error:(nnn, 50) java: incompatible types: inference variable T has incompatible bounds
equality constraints: T
upper bounds: java.lang.Comparable<? super T>
This compiles:
import java.util.*;
class Foo<T extends Comparable<? super T>> {
private Comparator<T> comparator;
public void someMethod(Comparator<T> comparator)
{
this.comparator = comparator; // no compile error
this.comparator = Comparator.<T>naturalOrder(); // <T> is optional, compiler can infer
}
}
The simplest way to think about it is this: you are trying to use type T with the Comparator interface, which imposes certain requirements on it (in particular it has that fancy recursive requirement that T must implement Comparable interface). You do not impose such requirement when genericising (?) your class, so compiler is not happy. Your requirements on T must be as strong as the class that you are using it with.
You are confused about what natural ordering method does. It just takes a class which implements Comparable and creates the default Comparator for it. No way around it -- you can't create a Comparator for something that is not Comparable.
You want TreeMap to require Comparable, but you can't, because it is a valid case to use something that is not comparable, as long as you have provided a Comparator. So TreeMap ends up not enforcing Comparable and just casts explicitly at runtime (and throws an exception).