Mayur Kulkarni
Mayur Kulkarni

Reputation: 1316

Java 8 Comparator comparing doesn't chain

Let's say I have a Pair class

public class Pair<P, Q> {
    public P p;
    public Q q;


    public Pair(P p, Q q) {
        this.p = p;
        this.q = q;
    }

    public int firstValue() {
        return ((Number)p).intValue();
    }

    public int secondValue() {
        return ((Number)q).intValue();
    }
}

And I wish to sort it, first by first value, then by second value. Now' if I do this

List<Pair<Integer, Integer>> pairList = new ArrayList<>();
pairList.add(new Pair<>(1, 5));
pairList.add(new Pair<>(2, 2));
pairList.add(new Pair<>(2, 22));
pairList.add(new Pair<>(1, 22));
pairList.sort(Comparator.comparing(Pair::firstValue));

Everything works well and good, the list is sorted by first values of pair, but if I do this

pairList.sort(Comparator.comparing(Pair::firstValue).thenComparing(Pair::secondValue));

It fails with error

Error:(24, 38) java: incompatible types: cannot infer type-variable(s) T,U
(argument mismatch; invalid method reference
  method firstValue in class DataStructures.Pair<P,Q> cannot be applied to given types
    required: no arguments
    found: java.lang.Object
    reason: actual and formal argument lists differ in length)

Ok,so it might not be able to infer the arguments, so if I do this

pairList.sort(Comparator.<Integer, Integer>comparing(Pair::firstValue)
                                          .thenComparing(Pair::secondValue));

It fails with error

Error:(24, 39) java: invalid method reference
non-static method firstValue() cannot be referenced from a static context

Why does it work for comparing() and not for comparing().thenComparing() ?

Upvotes: 13

Views: 4022

Answers (2)

shmosel
shmosel

Reputation: 50726

The error seems to be related to Pair's generic parameters. One workaround it to use an explicit type, as you've attempted:

pairList.sort(Comparator.<Pair>comparingInt(Pair::firstValue).thenComparingInt(Pair::secondValue));
//                       ^^^^^^

Note the comparingInt() which reduces the number of parameters you need to specify, and improves performance by avoiding boxing.

Another solution is to parameterize the type reference:

pairList.sort(Comparator.comparingInt(Pair<?,?>::firstValue).thenComparingInt(Pair::secondValue));
//                                        ^^^^^

Upvotes: 7

theKidOfArcrania
theKidOfArcrania

Reputation: 498

It should be:

pairList.sort(Comparator.<Pair, Integer>comparing(Pair::firstValue)
                                       .thenComparing(Pair::secondValue));

First type parameter refers to the type being passed to Comparator. Second type parameter refers to the type that comparator should effectively compare with.

Upvotes: 4

Related Questions