Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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>
like image 1000
davidbak Avatar asked Jul 04 '14 00:07

davidbak


People also ask

What does Comparator naturalOrder do?

The naturalOrder() method of Comparator Interface in Java returns a comparator that use to compare Comparable objects in natural order. The returned comparator by this method is serializable and throws NullPointerException when comparing null.

What does Comparator comparing do in Java?

comparator,” Java Comparator compares two Java objects in a “compare(Object 01, Object 02)” format. Using configurable methods, Java Comparator can compare objects to return an integer based on a positive, equal or negative comparison.

What does a Comparator return?

It returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned. By overriding compare( ), you can alter the way that objects are ordered. For example, to sort in a reverse order, you can create a comparator that reverses the outcome of a comparison.

What is a Comparator in Java Mcq?

Java Comparator interface is used to order the objects of a user-defined class. This interface is found in java. util package and contains 2 methods compare(Object obj1,Object obj2) and equals(Object element).


2 Answers

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

like image 148
MK. Avatar answered Nov 04 '22 06:11

MK.


I guess you have to implement Comparable to use Comparator.naturalOrder() in your class Foo in your example. You must have a method compareTo(Object o), that method is the one that implements the natural order so you don't need to store it in a variable.

I think you can use comparators in a class that doesn't implements the comparable interface at least in Java 8, so they don't implements compareTo(Object o) but you have to implement this

@FunctionalInterface
public interface Comparator<T>

This is from the Java 8 API

A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order. Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.

A way of implementing and initialising it:

private  Comparator<Operario> ComparatorOperario =
    (o, p)-> o.getNombre().compareTo(p.getNombre());

then you can have getters and setters for this variable so you can change the way of ordering

note that the class Operario doesn't implements Comparable, it uses a Comparator<Operario> that compares 2 attributes from the class Operario, in this case two Strings.

like image 43
Ismael_Diaz Avatar answered Nov 04 '22 08:11

Ismael_Diaz