Lambda 方法引用无效/引用不明确(javac/ecj行为差异)

Lambda 方法引用无效/引用不明确(javac/ecj行为差异),lambda,java-8,javac,type-inference,ecj,Lambda,Java 8,Javac,Type Inference,Ecj,使用Eclipse编译器for Java时,以下代码可以正确编译和运行 package org.sandbox; public final class ExceptionUtils { private ExceptionUtils(){} @FunctionalInterface public interface Runnable { void run() throws Exception; } @FunctionalInte

使用Eclipse编译器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 );
        }

    }
}
使用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
org\sandbox\Foo.java:22:error:uncheck的引用不明确
返回(Foo)取消选中(super::clone);
^
ExceptionUtils中的方法uncheck(可调用)和ExceptionUtils中的方法uncheck(可运行)都匹配
其中T是一个类型变量:
T扩展方法uncheck中声明的对象(可调用)
org\sandbox\Foo.java:22:错误:不兼容类型:无法推断类型变量T
返回(Foo)取消选中(super::clone);
^
(参数不匹配;方法引用无效。)
克隆()在对象中具有受保护的访问权限)
其中T是一个类型变量:
T扩展方法uncheck中声明的对象(可调用)
2个错误
这里似乎有两个问题

  • 受保护的方法不能用作方法引用
  • 仅基于返回类型(即T或void),无法选择正确的
    取消选中(…)
    方法
总体问题是“为什么会有行为差异?”,但可能可以分为:

  • 是javac对方法引用限制太严格了,还是Eclipse编译器在这里太松懈了
  • javac是否正确地确定了方法解析是不明确的,还是仅仅缺少Eclipse编译器的智慧来正确地确定应该选择哪个方法

这是javac编译器中的一个已知错误(请参阅),最终在OpenJDK 1.9ea-b89中修复。您可以下载并查看它是否正常编译您的代码。此错误仅影响方法引用。可以用lambda表达式替换它。它不会太长,在ECJ和javac中编译都很好:

return (Foo)uncheck( () -> super.clone() );

@studro,我猜无法决定调用哪个方法是原始错误的结果。如果您删除
取消选中
重载(只留下
可调用的
),您在javac中仍然会有一个编译错误。我不知道有针对的错误报告。我已经把这个链接添加到了它的答案中。@Holger,它是在那个问题的同一天(10月13日)提交的。因此,它可能是由问题作者或某个路人提交的。Webbug bug后来公开出现在OpenJDK JIRA上,因为它们是手动预建模的,因此当时可能仍然不可见。我注意到了日期,并考虑了提问者提交报告的可能性。但即便如此,添加链接对未来的读者还是有价值的。你是如何注意到错误报告的?@Holger,刚刚搜索了:这个错误是目前的第三个结果。没有魔法。也许这个问题可以作为那个问题的重复来解决?
return (Foo)uncheck( () -> super.clone() );