Comparator.reversed() type inference issue

Remi Forax forax at univ-mlv.fr
Tue Jun 9 08:18:14 UTC 2020


yep,
the inference is not smart enough.

So the way to fix the code is either to explicitly specify the type arguments 
  var rs1 = x.entrySet().stream().sorted(
      Map.Entry.<String, Long>comparingByValue().reversed()
    );

or to store the result of comparingByValue() in a variable (so it's a poly expression)
  Comparator<Map.Entry<String, Long>> comparator = Map.Entry.comparingByValue();
  var rs1 = x.entrySet().stream().sorted(comparator.reversed());

regards,
Rémi

----- Mail original -----
> De: "Tagir Valeev" <amaembo at gmail.com>
> À: "Attila Szegedi" <szegedia at gmail.com>
> Cc: "core-libs-dev" <core-libs-dev at openjdk.java.net>
> Envoyé: Mardi 9 Juin 2020 09:05:42
> Objet: Re: Comparator.reversed() type inference issue

> Hello!
> 
> This is more related to Java language specification and implementation
> than to core libraries, so compiler-dev is a more relevant mailing
> list. The current behavior perfectly follows the specification (JLS,
> 15.12):
> 
> A method invocation expression is a poly expression if all of the
> following are true:
> - The invocation appears in an assignment context or an invocation
> context (§5.2, §5.3). (1)
> - If the invocation is qualified (that is, any form of
> MethodInvocation except for the first), then the invocation elides
> TypeArguments to the left of the Identifier. (2)
> - The method to be invoked, as determined by the following
> subsections, is generic (§8.4.4) and has a return type that mentions
> at least one of the method's type parameters. (3)
> 
> If method invocation is a qualifier (in your case
> Map.Entry.comparingByValue() is a qualifier of reversed() call) then
> the context is neither assignment, nor invocation, hence (1) is not
> satisfied, hence it's a standalone expression, hence its type is
> determined solely by expression content. That's why to determine the
> type of Map.Entry.comparingByValue() we cannot look outside and must
> consider only the expression content. So its type is determined to be
> Comparator<Entry<Object,V#2>> (V#2 extends Comparable<? super V#2>),
> as we don't have any other information. The reversed() call is also
> standalone, as the reversed() method is not generic, so (3) is not
> satisfied. Thus its type is just the same as its qualifier type.
> 
> People asked many times whether it's possible to make this code
> working without explicit type arguments. However, it's not just a
> trivial patch to the compiler and the spec. It's huge amount of work
> that may introduce tons of bugs, compiler crashes (I would expect
> StackOverflowError appear often during the compilation), compiler
> performance regression, and so on. Also, loads of inconsistencies
> between compiler and IDE behavior. I personally don't work on Java
> compiler, but I sometimes investigate bugs inside the IntelliJ IDEA
> type inference procedure. Believe me, it's already insanely complex,
> and the fact that at very least we can exclude qualifiers from the
> equation is really relieving. So while I cannot say for the whole
> OpenJDK team, I hardly believe this could be implemented any time
> soon.
> 
> With best regards,
> Tagir Valeev.
> 
> On Sun, Jun 7, 2020 at 7:34 PM Attila Szegedi <szegedia at gmail.com> wrote:
>>
>> Hi folks,
>>
>> I'm teaching Java lately, and while teaching streams I use that good old
>> chestnut, the word count example. I'm baffled with some type inference woes,
>> though… trying to specify a reverse comparator using Comparator.reversed()
>> makes javac sad, e.g. this does not compile[1]:
>>
>>        Map<String, Long> x = someMap();
>>
>>        var rs1 = x.entrySet().stream().sorted(
>>            Map.Entry.comparingByValue().reversed()
>>        );
>>
>> On the other hand, using Collections.reverseOrder does compile:
>>
>>        var rs2 = x.entrySet().stream().sorted(
>>            Collections.reverseOrder(
>>                Map.Entry.comparingByValue()
>>            )
>>        );
>>
>> Happens on both Java 11 and 14. It’s just baffling because based on type
>> signatures, it seems reasonable the first form should work as well.
>>
>> Thanks for any enlightenment!
>>
>> Attila.
>>
>> ---
>> [1] -Xdiags:verbose on Java 14 says:
>> error: no suitable method found for sorted(Comparator<Entry<Object,V#1>>)
>>        var rs1 = x.entrySet().stream().sorted(
>>
>>    method Stream.sorted() is not applicable
>>      (actual and formal argument lists differ in length)
>>    method Stream.sorted(Comparator<? super Entry<String,Long>>) is not applicable
>>      (argument mismatch; Comparator<Entry<Object,V#2>> cannot be converted to
>>      Comparator<? super Entry<String,Long>>)
>>  where V#1,V#2 are type-variables:
>>    V#1 extends Comparable<? super V#1>
> >    V#2 extends Comparable<? super V#2>


More information about the core-libs-dev mailing list