Jiaming Li
Jiaming Li

Reputation: 227

Java Comparator type inference confusion

I have few questions for the following code sample.

class HypotheticComparators {
    class Pair<U,T> {
        private U left;
        private T right;
        public U getLeft() {
            return left
        }
        public T getRight() {
            return right;
        }
    }

    class User {
        private String name;
        private Integer age;
        public String getName(){
            return name;
        }
        public Integer getAge() {
            return age;
        }
    }

    public static void main(String[] args){
        List<Pair<LocalDate,LocalDate>> dataIntevals = new ArrayList<>();

        //Doesn't Compile
        Comparator<Pair<LocalDate,LocalDate>> pairComparator1  = Comparator.comparing(Pair::getLeft).thenComparing(Pair::getRight);

        //This Compile
        Comparator<Pair<LocalDate,LocalDate>> leftComparator = Comparator.comparing(Pair::getLeft);
        Comparator<Pair<LocalDate,LocalDate>> pairComparator2 = leftComparator.thenComparing(Pair::getRight));

        //This compile
        Comparator<User> userComparator1 = Comparator.comparing(User::getName).thenComparing(User::getAge);

        //This also compile
        Comparator<User> userNameComparator = Comparator.comparing(User::getName);
        Comparator<User> userComparator2 = userNameComparator.thenComparing(User::getAge);

    }
}

For this statement, Comparator<Pair<LocalDate,LocalDate>> pairComparator1 = Comparator.comparing(Pair::getLeft).thenComparing(Pair::getRight);, My initial idea of why it does not compile is that themComparaing cannot infer type from Comparator.comparing(Pair::getLeft). But i am not convinced by this idea beacuase looking at the source code of Comparator.comparing and thenComparing

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
        Function<? super T, ? extends U> keyExtractor)
{
    return thenComparing(comparing(keyExtractor));
}

Comparator.comparing(Pair::getLeft) can have type inference with T as Pair<LocalDate, LocalDate> , themComparing can also have a type inference with T as Pair<LocalDate, LocalDate>. Furthermore, if my idea is correct, why Comparator<User> userNameComparator = Comparator.comparing(User::getName); can compile without any problem. Am i missing anything here ?

Upvotes: 3

Views: 305

Answers (2)

Aleksandr
Aleksandr

Reputation: 438

The reason that the compiler can't infer the type of Pair::getLeft in the first case, but can in the second case, is because it uses the type of the variable to infer the type, but because you call thenComparing() right away, it doesn't have access to this information.

You can check this by having Comparator.comparing(Pair::getLeft); by itself on a new line, without saving it to a variable. The compiler will fail to infer the type.

This can be resolved by explicitly saying Pair<LocalDate,LocalDate>::getLeft.

Upvotes: 0

Andrew Rueckert
Andrew Rueckert

Reputation: 5225

The Java type-inferer can't be sure that the first .comparing is going to be accepting the same type that the second one is, so it guesses that the types for the first method are Object, Pair<LocalDate, LocalDate>, and Object doesn't implement Comparable. You can work around this by specifying the generic types for the method:

Comparator<Pair<LocalDate,LocalDate>> pairComparator1  = Comparator
    .<Pair<LocalDate, LocalDate>, LocalDate>comparing(Pair::getLeft)
    .thenComparing(Pair::getRight);

Upvotes: 2

Related Questions