The compiler does not display the system interface System.out :: println

I have an overloaded method that uses two different functional interfaces as parameters ( Runnble and Supplier ). System.out.println explicitly compatible only with Runnable , because it is a void method. However, the compiler still claims that the call is ambiguous. How is this possible?

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

Compiler Output:

 Error:Error:line (5)java: reference to wrap is ambiguous both method <R>wrap(java.util.function.Supplier<R>) in GenericLambdas and method wrap(java.lang.Runnable) in GenericLambdas match Error:Error:line (5)java: incompatible types: cannot infer type-variable(s) R (argument mismatch; bad return type in method reference void cannot be converted to R) 

Based on the second error ( argument mismatch, void cannot be converted to R ), should the compiler not eliminate the ambiguity of the call? Then this will take care of all the compiler errors (since it will not be ambiguous and will not try to convert void to R).

And why can you enable () -> {} and System.out::flush ? They have the same signature as System.out.println . Suppose System.out.println overloaded with versions that take an argument, but none of these overloaded versions matches either Supplier or Runnable , so I don’t see how important they are here.

EDIT:

This seems to compile and run using the Eclipse compiler . Which compiler is right, or is behavior allowed?

+8
java lambda java-8 type-inference functional-interface
source share
2 answers

Finding the appropriate version of println ignores the return type. See JLS 15.13.2 . It would be pointless to include it, because there cannot be two versions of a method with the same parameters, but with a different return type. But now the compiler has a problem: both Supplier#get and Runnable#run expect the same parameters (none). So there is println that will match for both. Keep in mind that at this point the compiler is only trying to find a method. The compiler basically faces the same problem as in this code:

 public static void main(String[] args) { test(System.out::println); } public static void test(Runnable r) {} public static void test(Consumer<String> r) {} 

println() corresponds to Runnable#run and println(String) corresponds to Consumer#accept . We did not present the target type, so the situation is ambiguous.

After selecting a method, the target type can be inferred correctly, and at this point, the return type is relevant. See JLS 15.13.2 . Thus, this code will of course fail:

 public static void main(String[] args) { wrap(System.out::println); } static <R> void wrap(Supplier<R> function) {} 

The compiler immediately throws an error when it detects an ambiguity. Although a bug report has been raised and accepted for this behavior, the comments indicate that the Oracle JDK can more correctly adhere to JLS than ECJ (despite its more pleasant behavior). A later bug report , raised at the end of this SO question, was resolved as β€œNot a Problem,” indicating that after internal debate, Oracle decided that javac correct.

+3
source share

Sounds like a bug in the javac compiler. The problem is the overloaded println() method. It has different implementations based on the type you are writing: int , long , String , etc. Thus, the expression: System.out::println has 10 methods to choose from. One of them can be displayed for Runnable , 9 others - Consumer<T> .

Somehow, the javac compiler cannot infer the correct method implementation from this expression. And wrap(() -> {}) compiles correctly because this expression has only one possible interpretation - Runnable .

I am not sure if it is allowed to have such expressions in accordance with the JLS rules. But the following code compiles correctly using javac (and works without runtime problems):

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

This cast seems to provide the required information to the compiler to correctly enter the type, which is strange. I did not know that expression expressions can be used in type inferences.

+2
source share

All Articles