Composing comparators: method reference works, the lambda equivalent does not.

Zhong Yu zhong.j.yu at
Sun Jan 5 10:04:04 PST 2014

`String::length` is an exact method reference, the compiler knows
precisely the function type it can represent, i.e. `String->int`.

On the other hand, the implicit lambda expression `s->s.length()` is
very vague - the compiler has no idea what it means by just looking at
it in isolation. The meaning of an implicit lambda expression must be
inferred from the context.

Usually type inference does an excellent job, for example
    Comparator<String> c = Comparator.comparing( s->s.length() );
however it doesn't work in all situations as your example shows.

You can break down your code to simpler expressions:
    Comparator<String> c = Comparator.comparing(s -> s.toString());
    c = c.thenComparing(s -> s.length());

Another solution, whenever type inference fails, is to manually
provide the type. In this case, either the type parameters of the
    Comparator<String> c =
or the lambda parameter types (i.e. explicit lambda expression)
    Comparator<String> c =
        Comparator.comparing((String s)->s.toString())
        .thenComparing(s -> s.length());

Explicit lambda expressions are context-free when determining their
function types. Explicit declaration of (Type arg) not only helps
compilers, but also helps human readers tremendously. It's probably a
good practice to use explicit lambda expressions over implicit ones
whenever possible.

Zhong Yu

On Sun, Jan 5, 2014 at 7:01 AM, Paulo Silveira
<paulo.silveira at> wrote:
> While trying to compose comparators, I am having strange problems
> using lambdas instead of method references.
> This one compiles (I know the resulting comparator does not make sense):
> Comparator<String> c = Comparator.comparing(String::toString)
>                             .thenComparing(String::length);
> This one does not compile:
> Comparator<String> c = Comparator.comparing(s -> s.toString())
>                               .thenComparing(s -> s.length());
> (compilation error here:
> It seems the compiler is expecting a Function<Object, String> instead
> of Function<String, String>. The latest Goetz' state of lambda says
> this should work, but it gives me the same compilation error:
> Comparator<Person> c = Comparator.comparing(p -> p.getLastName())
>                                  .thenComparing(p -> p.getFirstName());
> My build is 1.8.0-ea-b121
> Another quick question: when would I need to use
> Comparator.comparingInt if works just fine to
> create a Comparator<Integer>? The only difference I can see is to
> avoid NPEs through
> Regards
> --
> Paulo Silveira

More information about the lambda-dev mailing list