Comparator.reversed() type inference issue
Tagir Valeev
amaembo at gmail.com
Tue Jun 9 07:05:42 UTC 2020
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