DamianGDO
DamianGDO

Reputation: 469

Java - Definition of the comparing method of Comparator class

I would like to understand the definition of the method comparing of Comparator class. I will leave some code in case it helps with the explanation. I'm working with a class called Person that basically stores a name and a last name. This data can be retrieve with get methods.

public class Person {

    private final String name;
    private final String lastname;

    public Person(String name, String lastname) {
        this.name = name;
        this.lastname = lastname;
    }

    public String getName() { return name; }

    //...
}

I then created a list of Persons:

List<Person> list = Arrays.asList(
    new Person("Juan", "García"),
    new Person("Ana", "Martínez"),
    ...
);

I've been testing different ways to sort this list of Persons. Among other possibilities, I found this one:

list.sort(Comparator.comparing(Person::getName));

I understand what these lines do. Basically, this list is sorted using a Comparator that compares using a sort key (a Person's name). Such key is extracted using a reference to the getName method belonging to the Person class.

However, I also like to understand what's going on behind the scenes. My problem is with the comparing method. The Java documentation defines such method like this:

static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

Particularly, I'm struggling with the generic types definition: <T,U extends Comparable<? super U>>; and the definition of the parameter's type: Function<? super T,? extends U>

T represents the comparator type, while U represents the key type, right? So... Why U needs to extends Comparable<? super U>, which in turns uses any superclass of U?

In the argument, the function works with an object belonging to any superclass of T and returns an object belonging to any subclass of U (<? super T,? extends U>), but... why though?

I hope my doubt is clear. Also, sorry for the long explanation.

Upvotes: 3

Views: 491

Answers (2)

Sweeper
Sweeper

Reputation: 272895

I see three main confusions here:

Why U extends Comparable<? super U>?

This is a constraint on U (the key's type), specifying what specific types U can be. It says that U must be comparable. Makes sense, right? The part in the angle brackets specifies "what things can U be compared to?" For example, Comparable<Animal> means "comparable to animals". The constraint says "U must be comparable to some supertype of U".

You might be wondering why doesn't it just say "U must be comparable to U", i.e. U extends Comparable<U>. This is because we are trying to make our method as flexible as possible, i.e. we try to accept as many different Us as possible. If I have a Dog that can be compared to an Animal (implements Comparable<Animal>), it makes logical sense that the dog can also be compared to another Dog. However, since Dog implements Comparable<Animal> rather than Comparable<Dog>, it doesn't satisfy U extends Comparable<U>, which is why we have to use U extends Comparable<? extends U>.

Why does the Function take ? super T?

Imagine I have a key extractor that takes an Object, and I return its toString() output as the key to compare (this is a very uncommon thing to compare, but for the sake of this example, bear with me :D)

Function<Object, String> stringExtractor = Object::toString;

Now I want a comparator that compares animals. Can I use this stringExtractor as the argument for comparing? Of course I can:

Comparator<Animal> comparator = Comparator.comparing(stringExtractor);

All animals have a toString method, as they are subclasses of Object after all! This is why the key extractor parameter takes functions that take a superclass of T - to allow us to pass things like the above. If it were Function<T, ? extends U>, we wouldn't be able to do Comparator.comparing(stringExtractor).

Again, the idea is that we want comparing to work with as many things as possible.

Why does the Function return ? extends U?

This is actually quite unnecessary. Unless you explicitly specify T and U like this:

Comparator.<Foo, Animal>comparing(Foo::getDog);

It makes no difference whether it is Function<? super T, U> or Function<? super T, ? extends U>. See this question for more details. That question is about thenComparing, which has the signature as comparing.

Upvotes: 2

Nowhere Man
Nowhere Man

Reputation: 19565

Let's consider the signature of the mentioned method comparing static <T,U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)

  • T is the type of object being compared, in the mentioned case it is Person

  • U extends <Comparable<? super U>> describes the type used to implement actual comparison - that's why this type must be Comparable - in this case it is String

  • Function<? super T,? extends U> keyExtractor - keyExtractor is a function which "converts" objects of class T (Person) to comparable objects of class U (String) - in this class it is a getter of name defined in class Person.

Upvotes: 2

Related Questions