无效的方法引用/不明确的引用(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

看来这里有两个问题

总的问题是"why is there a behaviour difference?",但或许可以分解为:

这是 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() );