The following code compiles and runs correctly when using the Eclipse Compiler for Java.
package org.sandbox;
public final class ExceptionUtils
{
private ExceptionUtils(){}
@FunctionalInterface
public interface Runnable
{
void run() throws Exception;
}
@FunctionalInterface
public interface Callable<T>
{
T call() throws Exception;
}
public static void uncheck( final Runnable r )
{
try
{
r.run();
}
catch( final Exception e )
{
throw new RuntimeException( e );
}
}
public static <T> T uncheck( final Callable<T> c )
{
try
{
return c.call();
}
catch( final Exception e )
{
throw new RuntimeException( e );
}
}
}
...
package org.sandbox;
import static org.sandbox.ExceptionUtils.uncheck;
public class Foo
{
private String bar;
public String getBar()
{
return bar;
}
public void setBar( final String bar )
{
this.bar = bar;
}
@Override
public Foo clone()
{
return (Foo)uncheck( super::clone );
}
}
When compiled using javac, the following errors are emitted:
org\sandbox\Foo.java:22: error: reference to uncheck is ambiguous
return (Foo)uncheck( super::clone );
^
both method <T>uncheck(Callable<T>) in ExceptionUtils and method uncheck(Runnable) in ExceptionUtils match
where T is a type-variable:
T extends Object declared in method <T>uncheck(Callable<T>)
org\sandbox\Foo.java:22: error: incompatible types: cannot infer type-variable(s) T
return (Foo)uncheck( super::clone );
^
(argument mismatch; invalid method reference
clone() has protected access in Object)
where T is a type-variable:
T extends Object declared in method <T>uncheck(Callable<T>)
2 errors
It appears there are two issues here
uncheck(...)
method based solely on the return type (ie. either T or void)The overall question is "why is there a behaviour difference?", but perhaps it can be broken down into:
This is a known bug in javac compiler (see JDK-8139836) which is finally fixed in OpenJDK 1.9ea-b89. You may download the Java9 early access build and see that it compiles your code normally. This bug affects method references only. You can replace it with lambda expression. It's not much longer and compiles fine in both ECJ and javac:
return (Foo)uncheck( () -> super.clone() );