javagenericsjava-8comparatorbounded-wildcard

java.util.Comparator.naturalOrder takes a <T extends Comparable<? super T>> and returns a Comparator<T> - why?


(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:

  1. How is Comparator.naturalOrder properly used?
    • And can I do what I want which is have a field where I store either a user-supplied comparator or the naturalOrder comparator?
  2. Given that most collection classes (in the framework) are parameterized on 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>

Solution

  • 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).