39

I was playing around with examples from http://www.concretepage.com/java/jdk-8/java-8-unaryoperator-binaryoperator-example.

What I find really confusing is that when I mistakenly put a wrong type into one of generics when forming Collectors, java compiler gives me a very misleading message:

Non-static method cannot be referenced from a static context

My error has nothing to do with static vs instance context in reality:

Map<String, Map<Integer, Integer>> mapOfStudents = list.stream().collect(Collectors.groupingBy(Student::getClassName,
            Collectors.toMap(Student::getName, Student::getAge)));

My mistake is in generic return type. When I correct it and put:

Map<String, Map<String, Integer>> mapOfStudents

everything goes back to normal.

Can someone explain the reason behind such a confusing error message? I'm sure the is a good one, but I fail to grasp it.

EDIT:

~$ java -version
openjdk version "1.8.0_121"
OpenJDK Runtime Environment (build 1.8.0_121-8u121-b13-0ubuntu1.16.04.2-b13)
OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
yuranos
  • 8,799
  • 9
  • 56
  • 65
  • I get a very different message with `javac` (much clearer). Although I have had similarly bad error messages in Eclipse when using method references/lambdas. – Jorn Vernee Feb 13 '17 at 09:59
  • 1
    Please provide the exact make and version of the java compiler, as type inference is one of the things that has been changing quite frequently. – biziclop Feb 13 '17 at 10:00
  • See Edit section for details. I'm running Intellij Idea 2016.3 – yuranos Feb 13 '17 at 10:13
  • Perhaps it is looking for a non-static getName that returns an Integer, but the compiler breaks off the search as only a static method would qualify. – john16384 Feb 13 '17 at 10:32
  • But I am not referring to 'this'. Java compiler should be smart enough to understand what a method reference to a arbitrary object is. – yuranos Feb 13 '17 at 10:52
  • 1
    Note the similar message [here](http://stackoverflow.com/q/42127735/1553851). – shmosel Feb 13 '17 at 19:25
  • 1
    @JornVernee, can you give an example for a bad message in Eclipse? For this example, Eclipse (head) says: "Type mismatch: cannot convert from Map> to Map>", which looks very good to me. – Stephan Herrmann Feb 14 '17 at 14:19
  • @StephanHerrmann Nothing I can reproduce a at the moment. I tried the question's code, and it looks like you can't use quick-fix to add in the missing methods into `Student` when using method references (a feature that I use all the time). That might actually be the annoyance I was remembering. – Jorn Vernee Feb 14 '17 at 15:04
  • I remember seeing similar misleading error messages in the past, but I can’t reproduce it with the question’s example. Generally, the answer is that the main focus of compiler development is at compiling correct code correctly, not at analyzing the cause of an error. Sometimes, it’s really impressive what a small mistake like a single misplaced character (say a comma or bracket) can cause. Correctly analyzing the problem of a knowingly wrong source code could require an entirely different program… – Holger Feb 14 '17 at 17:35
  • @StephanHerrmann indeed, ecj behaves much better here, great job! – Tagir Valeev Feb 15 '17 at 03:14

1 Answers1

46

First it should be noted, that the message is issued not by java compiler (javac), but by IntelliJ IDEA. You can see javac messages in "Messages Build" window when you actually launch a build process. What you see in editor window is messages generated by IDEA itself and they could differ.

The error message is misleading due to implementation of method reference resolution in IntelliJ IDEA. It considers non-static method reference to be resolved only if number of corresponding SAM (single abstract method) arguments equals to number of method arguments plus one and the first SAM argument type is compatible with method containing class. See the implementation (also isSecondSearchPossible method above, some additional magic is performed for varargs methods).

It works correctly if your program has no errors. However if you have a mismatched type, the generic arguments of the Function passed into toMap cannot be substituted, so it remains Function<T, R>, and its apply method first argument is simply T which does not correspond to the type Student. Thus so-called "second search" fails and IDEA thinks that the method is referenced from static context. While both static and non-static context are not applicable here, non-static context matches your method better, at least according to the number of arguments as getName() method receives no arguments. On the other hand, IDEA logic is "if non-static context is not applicable, then it's a static context", hence the error message.

I would consider this as a bug, or at least as a usability problem. I've just logged it here based on similar question. Hopefully we will fix it.

Disclaimer: I'm IntelliJ IDEA developer.

Update: fixed in IDEA 2017.2.

Community
  • 1
  • 1
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
  • 4
    If it doesn't agree with `javac` it's a bug. – user207421 Feb 15 '17 at 03:20
  • 4
    @EJP, it agrees that the program is incorrect, but error message differs. Java language specification specifies what is correct program, but it does not specify exactly how should error messages look for incorrect programs. Note that error messages in Eclipse compiler are also different, and in this case are actually even better, than javac messages in terms that you can easier spot the actual error. Would you consider this as a bug in Eclipse compiler? :-) – Tagir Valeev Feb 15 '17 at 03:24
  • 3
    To me, "non-static & static references" is a far cry from issue of "generic types doesn't match". – Jackie Aug 02 '17 at 13:40
  • 12
    Recent versions of IDEA still have the same issue, at least for map-reduce error messages. tested with 2018.3.6 and 2019.2 – Kayvan Tehrani Oct 16 '19 at 08:03