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 <[email protected]> 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>
