Composing comparators: method reference works, the lambda equivalent does not.
Zhong Yu
zhong.j.yu at gmail.com
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
method
Comparator<String> c =
Comparator.<String,String>comparing(s->s.toString())
.thenComparing(s->s.length());
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 caelum.com.br> 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: https://gist.github.com/peas/8267900)
>
> 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 Comparator.compare works just fine to
> create a Comparator<Integer>? The only difference I can see is to
> avoid NPEs through Integer.compare(...).
>
> Regards
> --
> Paulo Silveira
> www.caelum.com.br
> www.casadocodigo.com.br
> www.alura.com.br
>
More information about the lambda-dev
mailing list