Reputation: 469
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
Reputation: 272895
I see three main confusions here:
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 U
s 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>
.
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.
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
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