无效的方法引用/不明确的引用(javac/ecj 行为差异)
Invalid method reference / ambiguous reference (javac / ecj behaviour difference)
使用 Java 的 Eclipse 编译器时,以下代码可以正确编译和运行。
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 );
}
}
使用 javac 编译时,出现以下错误:
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
看来这里有两个问题
- 受保护的方法不能用作方法引用
- 不可能 select 仅基于 return 类型(即 T 或 void)的正确
uncheck(...)
方法
总的问题是"why is there a behaviour difference?",但或许可以分解为:
- 是javac对方法引用限制太严格了,还是Eclipse编译器松懈了?
- javac 是否正确确定方法解析不明确,或者它只是缺乏 Eclipse 编译器的智慧来正确确定应该 selected 的方法?
这是 javac 编译器中的一个已知错误(请参阅 JDK-8139836) which is finally fixed in OpenJDK 1.9ea-b89. You may download the Java9 early access build 并看到它可以正常编译您的代码。此错误仅影响方法引用。您可以将其替换为 lambda 表达式。它不会太长并编译在 ECJ 和 javac 中都很好:
return (Foo)uncheck( () -> super.clone() );
使用 Java 的 Eclipse 编译器时,以下代码可以正确编译和运行。
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 );
}
}
使用 javac 编译时,出现以下错误:
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
看来这里有两个问题
- 受保护的方法不能用作方法引用
- 不可能 select 仅基于 return 类型(即 T 或 void)的正确
uncheck(...)
方法
总的问题是"why is there a behaviour difference?",但或许可以分解为:
- 是javac对方法引用限制太严格了,还是Eclipse编译器松懈了?
- javac 是否正确确定方法解析不明确,或者它只是缺乏 Eclipse 编译器的智慧来正确确定应该 selected 的方法?
这是 javac 编译器中的一个已知错误(请参阅 JDK-8139836) which is finally fixed in OpenJDK 1.9ea-b89. You may download the Java9 early access build 并看到它可以正常编译您的代码。此错误仅影响方法引用。您可以将其替换为 lambda 表达式。它不会太长并编译在 ECJ 和 javac 中都很好:
return (Foo)uncheck( () -> super.clone() );