Composing comparators: method reference works, the lambda equivalent does not.
Brian Goetz
brian.goetz at oracle.com
Sun Jan 5 10:17:04 PST 2014
Also, you want to use .thenComparingInt() to avoid unnecessary boxing.
On 1/5/2014 1:04 PM, Zhong Yu wrote:
> `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