JDK-8144169 : Unable to infer correct functional interface for overloaded void method
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_8
  • CPU: x86
  • Submitted: 2015-10-15
  • Updated: 2015-12-07
  • Resolved: 2015-11-27
Description
FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10240]

A DESCRIPTION OF THE PROBLEM :
Consider an overloaded method that takes two different functional interfaces as parameters (`Runnble` and `Supplier`). `System.out.println` is clearly only compatible with `Runnable`, because it is a `void` method. javac agrees, reporting an error that explicitly notes that the return type of `Supplier` cannot be converted to void. Yet javac still claims in a separate error that the call is ambiguous and matches both `Runnable` and `Supplier`.

Note that both lambda `() -> {}` and method reference `System.out::flush` are able to resolve. The difference appears to be that `System.out.println` is overloaded with versions that take an argument. As none of those overloaded versions match either `Supplier` or `Runnable`, it is unclear how they would be relevant here.

Another note is that this does resolve correctly and compile with the Eclipse compiler.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
>javac.exe -cp . GenericLambdas.java

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Ability to successfully compile and run. javac should select this overloaded version of wrap:  
static void wrap(Runnable function){}
ACTUAL -
javac is unable to compile the class

ERROR MESSAGES/STACK TRACES THAT OCCUR :
GenericLambdas.java:5: error: reference to wrap is ambiguous
        wrap(System.out::println);
        ^
  both method <R>wrap(Supplier<R>) in GenericLambdas and method wrap(Runnable) in GenericLambdas match
  where R is a type-variable:
    R extends Object declared in method <R>wrap(Supplier<R>)
GenericLambdas.java:5: error: incompatible types: cannot infer type-variable(s) R
        wrap(System.out::println);
            ^
    (argument mismatch; bad return type in method reference
      void cannot be converted to R)
  where R is a type-variable:
    R extends Object declared in method <R>wrap(Supplier<R>)
2 errors

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
    import java.util.function.Supplier;
    
    public class GenericLambdas {
        public static void main(String[] args) {
            wrap(System.out::println);   // Compiler error here
            wrap(() -> {});              // No error
        }
    
        static <R> void wrap(Supplier<R> function) {}
    
        static void wrap(Runnable function) {}
    }
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Explicitly casting the method reference to `Runnable`:

wrap((Runnable) System.out::println);


Comments
for similar cases it could be a good idea to report the bug to the Eclipse bug DB and add a link here
07-12-2015

This is not an issue - in the case of the lambda, note that the lambda is _explicit_ i.e. it's known to have no arguments and to return void, and those information can be used to drive overload resolution. The method reference is inexact, as multiple overloads for System.out.println exists. It is true that all such overloads return void - but the spec doesn't say anywhere that this should/could be used as a disambiguation strategy. More specifically, 15.2.2.2 says that: 15.12.2.2. Phase 1: Identify Matching Arity Methods Applicable by Strict Invocation "An argument expression is considered pertinent to applicability for a potentially applicable method m unless it has one of the following forms: An implicitly typed lambda expression (��15.27.1). An inexact method reference expression (��15.13.1)." And then: "Let m be a potentially applicable method (��15.12.2.1) with arity n and formal parameter types F1 ... Fn, and let e1, ..., en be the actual argument expressions of the method invocation. Then: If m is a generic method and the method invocation does not provide explicit type arguments, then the applicability of the method is inferred as specified in ��18.5.1. If m is a generic method and the method invocation provides explicit type arguments, then let R1 ... Rp (p ��� 1) be the type parameters of m, let Bl be the declared bound of Rl (1 ��� l ��� p), and let U1, ..., Up be the explicit type arguments given in the method invocation. Then m is applicable by strict invocation if both of the following are true: For 1 ��� i ��� n, if ei is pertinent to applicability then ei is compatible in a strict invocation context with Fi[R1:=U1, ..., Rp:=Up]. For 1 ��� l ��� p, Ul <: Bl[R1:=U1, ..., Rp:=Up]. If m is not a generic method, then m is applicable by strict invocation if, for 1 ��� i ��� n, either ei is compatible in a strict invocation context with Fi or ei is not pertinent to applicability." So in all cases, arguments not pertinent to applicability are effectively skipped by the overload check - hence the ambiguity.
27-11-2015

This is an issue, Eclipse compiles properly, where as javac run through windows prompt and linux terminal gives the below error. wrap(System.out::println); // Compiler error here (argument mismatch; bad return type in method reference void cannot be converted to R) where R is a type-variable: R extends Object declared in method <R>wrap(Supplier<R>) Expected it should compile properly. Verified in below versions 8 - Fail 8u60 - Fail 8u65 - Fail 8u72 - Fail 9 ea (b93) - Fail
27-11-2015