How do I interpret the argument of method comparing(Function<? super T,? extends U> keyExtractor)?
Asked Answered
S

2

5

Full signature of method:

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

I'm learning lambda expressions and I have this piece of code that compare a List of employees and order them by the name field:

List<Employee> employees = new ArrayList<>();

Collections.sort(employees, Comparator.comparing(Employee::getName));

The code works fine but I have looked at Comparator functional interface in docs and I have found the signature of the method "comparing()".

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

I don't get the parameter of comparing(). How do I know that the parameter accepts a lambda exprssion? And how is interpreted the constrains: <? super T,? extends U> keyExtractor?

I know that super means that ? has to be type T or above in the hierarchical inheritance, and ? also must be of type U and below in the hierarchical inheritance. But how can we traduce that in my example?

It can be interpreted like this: ? must be of type Employees and above in inheritance chain and the name field must be type Employees or below in inheritance chain? Or ? must be type Array List and above and ? must be type Employees List and below?

Semination answered 27/12, 2020 at 17:38 Comment(3)
It can be interpreted like this: ? must be of type Employees and above in inheritance chain and the name field must be type Employees or below in inheritance chain?Semination
? is not the same as a generic type such as T and it doesn't mean that both your types of Function has to be in the hierarchy of Employee. They are anything of two different types T and U in simple words, where the T would be related to Employee based on the types of method type inference since it's been called upon a List<Employee>.Horatius
? is a wildcard of type T and U, and T and U are some type of objects that need to be constraint like in the method signature, no? But I want to know how to interpret this signature so I will know how and why to use it, not to learn by hart.Semination
L
5

How do I know that the parameter accepts a lambda expersion?

By looking at the interface of the argument it accepts. In this case, the argument type is Function, which is a functional interface (this name does not really have any connection to the name of the interface - you can name your interfaces however you want). A functional interface is an interface that has only one unimplemented function (the additional distinction comes from a fact that interfaces can have default implementations).

Take a look at Function:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

There is only one unimplemented method - it's called apply. When you create a lambda that's supposed to be a Function, that lambda's body will be used to implement apply.

You may be confused about the @FunctionalInterface - it's not required. It's just an annotation for convenience purposes.


About the <? super T,? extends U>, these are constraints on generic types. It means that said Function needs an argument that's a supertype T and will return something that is derived from U. This is a fairly complicated topic. You can read more about it here.

Lituus answered 27/12, 2020 at 17:42 Comment(9)
You mean that "Function" from the argument tell us that is receiving a functional intreface? And how about the restrictions : <? super T,? extends U> keyExtractor?Semination
Yes, but keep in mind that Function is a functional interface not because it's named Function, but because an interface that's called Function has only one method that is not defaulted. It's called apply() and lambda is used to implement that method in an inlined way.Lituus
Ok, if I have the word Function it denotes a functional interface, whatever functional interface it is.Semination
Yes I know what is a functional interface, and I know that the annotation is put for clearing purpose, but like a beginner I want to learn how to read the argument.Semination
I know that super means that ? has to be type T or above in the hierarchical inheritance, and ? also must be of type U and below in the hierarchical inheritance. But how can we traduce that in my example?Semination
It can be interpreted like this: ? must be of type Employees and above in inheritance chain and the name field must be type Employees or below in inheritance chain?Semination
What "name field"?Lituus
every employee from the List employees has a field "name" and a getter in respect of this.Semination
You don't have to return anything Employee related in Comparators. You just need an extractor and something comparable (in this specific case). I am not sure I completely understand your follow up questions. I think it may be better for you to digest the question and answers I linked and then ask another question.Lituus
S
0

Full signature of the method:

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

Citing from docs:

Accepts a function that extracts a Comparable sort key from a type T, and returns a Comparator that compares by that sort key.

So, to this point we know that comparing() method accept as parameter a function, and that is a Functional Interface that @Fureeish has explained in the previous answer. And because lambda expressions are build around functional interfaces, we know that we can use it as parameter.

Type Parameters:

T - the type of element to be compared U - the type of the Comparable sort key

T, in regard of the definition above is of type Employee, because we want to compare type of employees or objects that their class are super classes of Employee. ? super T, means that placeholder ? must be type T or above in the chain of inheritance.

U, is type String, it's the "name" field, and String implements Comparable (see docs), as to respect the method definition:

public static <T, U extends Comparable<? super U>> Comparator

The parameter method return a Comparator that compares by an extracted key.

Semination answered 30/12, 2020 at 16:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.