10

I am currently migrating a project to JDK 11 and compiling it with Maven. However, Maven throws a wobbly over a single method reference (it doesn't have issues elsewhere). The method in question looks like this:

public class MyThing {

    boolean something = true;

    // ...

    public boolean isSomething() {
        return something;
    }

    // ...
}

And the call to the above method looks more or less like this:

return methodThatGetsOptionalDefinition(aString)                 // Optional<Definition>
        .map(definition -> defenition.getMyThing(anotherString)) // Optional<MyThing>
        .map(MyThing::isSomething)                               // Optional<Boolean>
        .orElse(true);

Compiling this, Maven throws the following message:

> mvn clean install -Pdist-snapshot -DskipTests
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.0:compile (default-compile) on project XXXXX: Compilation failure
[ERROR] /D:/XXXXX.java:[63,38] incompatible types: invalid method reference
[ERROR]     method isSomething in class XXXXX.MyThing cannot be applied to given types
[ERROR]       required: no arguments
[ERROR]       found: java.lang.Object
[ERROR]       reason: actual and formal argument lists differ in length
[ERROR]
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.

If I expand the method reference like so, it compiles without a problem:

return methodThatGetsOptionalDefinition(aString)
        .map(definition -> defenition.getMyThing(anotherString))
        .map(myThing -> myThing.isSomething())
        .orElse(true);

Typing the Optional also works:

return methodThatGetsOptionalDefinition(aString)
        .<MyThing>map(definition -> defenition.getMyThing(anotherString))
        .map(myThing -> myThing.isSomething())
        .orElse(true);

The same error occurs when compiling the project using IntelliJ with both Open JDK versions 11.0.1 and 11.0.2. Ironically, IntelliJ complains that the "Lambda can be replaced with method reference". The problem also occurs when using a different "language mode".

<plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>11</source>
        <target>11</target>
    </configuration>
</plugin>

Does anyone have any idea why this might be happening?

Some of the specific implementation details have been obfuscated.

Druckles
  • 3,161
  • 2
  • 41
  • 65
  • 4
    I cannot reproduce. With the proper assumptions about `something`, `thingyService.getMyThing`, `name` and the return type of the enclosing method your code compiles fine on my JDK 11. [A Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve), please? – Ole V.V. Mar 22 '19 at 12:37
  • Just a random shot: `Optional.ofNullable(thingyService.getMyThing(name))` may be another possible fix. – Ole V.V. Mar 22 '19 at 12:42
  • A minimal example is unfortunately not possible. As mentioned, the method reference works in several other places (including in a simple JUnit test). And it compiles perfectly on JDK 8. I have, however, edited the post to reduce assumptions and be more explicit about the code causing the problem. I will have a look to see if explitly typing solves the problem. – Druckles Mar 22 '19 at 12:52
  • Interestingly typing "solves" the problem in so far as it compiles. But IntelliJ still believes that the explicit type arguments can be inferred (which they can). This sounds like a problem with the JDK. – Druckles Mar 22 '19 at 13:02
  • Have you checked in IntelliJ if the project configuration for the SDK is correctly set to JDK11 ? Furthermore have you set `..` and `..` or `11` correctly? – khmarbaise Mar 23 '19 at 08:42
  • Yes and yes. I have added the plugin configuration to the question description. – Druckles Mar 25 '19 at 09:35
  • Same error here, similar optional mapping chain. I was also not able to create a minimal example. The problem exists also with JDK 12 – Grisu118 Apr 23 '19 at 12:28
  • 2
    Please upvote if you stumble across the same problem. Some people apparently dislike the "not easily reproducable" nature of the problem ;-) – Druckles Apr 24 '19 at 07:12
  • @Druckles I ran at the same issue. I have an interface with a default method and two classes that implements this interface. And using the method without args in a stream as method reference produces the same error. – Dragon Aug 03 '21 at 09:09
  • Btw, I use AdoptOpenJDK (build 11.0.10+9) – Dragon Aug 03 '21 at 09:18

1 Answers1

-1

It has to do with the Type definition of the map method.

In order to provide a method reference, the method must have a matching Type definition. Stream map has a function method, but yours is a producer function.

Function = (T) -> R
Producer = () -> R

So, Method::isSomething() is not compatible with Stream.map, but your inline closure myThing -> myThing.isSomething() is..

Niels Bech Nielsen
  • 4,777
  • 1
  • 21
  • 44
  • 1
    I'm afraid this is wrong. `() -> R` can be interpreted as `(this) -> R` for functional interfaces. Example is above: it works again if the chain is typed. It also compiled in JDK 8 with no problem and works in a minimal example. – Druckles Oct 11 '21 at 14:21