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

Paulo Silveira paulo.silveira at caelum.com.br
Sat Jan 11 07:20:46 PST 2014


thanks guys. now I have another inference problem:

strings.sort(Comparator.naturalOrder()); // works fine, sure

strings.sort(Comparator.naturalOrder().reversed()); // it does not work

strings.sort(Comparator.<Comparable>naturalOrder().reversed()); // it
does work, unchecked warning

Is it the same case? I shouldn't expect it to compile without forcing
the generic type?
--
Paulo Silveira
www.caelum.com.br
www.casadocodigo.com.br
www.alura.com.br


2014/1/5 Brian Goetz <brian.goetz at oracle.com>:
> 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