Java 8方法引用:在编译时验证方法

时间:2013-12-30 13:45:05

标签: java java-8

我想使用Java 8的新方法引用在编译时提供一些代码的更多验证。

假设我有validateMethod方法需要一个参数:要验证的“方法”。例如:

validateMethod(foo, "methodA");

此处,该方法将验证foo#methodA()是否存在,在运行时

使用方法参考,我希望能够:

validateMethod(foo::methodA);

因此,该方法的存在将在编译时验证

问题在于,似乎将方法引用具有分配给功能接口。例如,这个:

Object dummy = foo::methodA;

生成错误:“此表达式的目标类型必须是功能接口”。

如果我创建一个与methodA方法具有兼容签名的功能界面,它可以工作:

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
MyFunctionalInterface dummy = foo::methodA;

现在foo#methodA()的存在在编译时得到验证,这就是我想要的!

但是...

假设validateMethod不知道它必须验证的方法的签名。那么它仍然可以实现吗?

让我们假装我们不关心歧义和重载方法。在Java 8中是否可以实现某种方法来触发任何方法引用的验证?

例如:

public class Foo
{
    public String methodA()
    {
        return "methodA";
    }

    public String methodB(String str)
    {
        return "methodB";
    }

    public String methodC(String str, int nbr)
    {
        return "methodC";
    }
}

Foo foo = new Foo();
validateMethod(foo::methodA); // Compile
validateMethod(foo::methodB); // Compile
validateMethod(foo::methodC); // Compile
validateMethod(foo::methodD); // Error!

是否可以以接受任何方法引用的方式实现validateMethod,因此该方法的存在将在编译时验证?

我试过了:

public void validateMethod(Object obj){}

但它不起作用:“此表达式的目标类型必须是功能接口

这样可行:

@FunctionalInterface
public interface MyFunctionalInterface
{
    public String run();
}
public void validateMethod(MyFunctionalInterface param){}

但仅适用于methodA类的Foo,因为它的签名(无参数)与功能接口的方法签名兼容!

是否可以以这样一种方式实现功能接口MyFunctionalInterface,使任何方法引用成为有效参数,从而在编译时进行验证?

您认为在编译时验证方法是否存在的任何其他方式?

3 个答案:

答案 0 :(得分:13)

您似乎正在尝试使用方法引用,它们实际上是 lambda表达式的短手,作为方法文字,是对方法的语法引用(很像Foo.class是对Foo类实例的语法引用)。这两个是不一样的,这就是你遇到阻抗的原因。你尝试的是滥用javac编译器完全拒绝的语言功能。

不幸的是,Java中没有方法文字,因此您必须通过其他方式描述该方法,例如: Reflection,MethodHandles.Lookup等我觉得很容易想出这种反射检查器,甚至建立注释处理器来检查编译时给定方法的存在。

答案 1 :(得分:3)

您可以尝试以下内容:

public class Validate {
    public String methodA() { return "methodA"; }
    public String methodB(String s) { return "methodB"; }
    public String methodC(String s, int n) { return "methodC"; }

    public static void main(String[] args) {
        Validate foo = new Validate();
        validateMethod(foo::methodA);
        validateMethod(foo::methodB);
        validateMethod(foo::methodC);
    }

    private interface Func0 { void method(); }
    private interface Func1<T> { void method(T t); }
    private interface Func2<T, U> { void method(T t, U u); }
    private interface Func3<T, U, V> { void method(T t, U u, V v); }

    public static void validateMethod(Func0 f) { }
    public static <T> void validateMethod(Func1<T> f) { }
    public static <T, U> void validateMethod(Func2<T, U> f) { }
    public static <T, U, V> void validateMethod(Func3<T, U, V> f) { }
}

但是您需要为每个需要验证的方法提供validateMethod的接口和重载。此外,如果验证方法被重载,它将无法工作,除非您添加显式强制转换:

    // if there are two methodA's:
    public String methodA() { return "methodA"; }
    public String methodA(long x) { return "methodA"; }

        validateMethod(foo::methodA); // this doesn't work
        validateMethod((Func0)foo::methodA); // this does
        validateMethod((Func1<Long>)foo::methodA); // so does this

答案 2 :(得分:-1)

interface Method { public Object runMethod(Object... args); }工作吗?我看到的唯一潜在问题是处理原始类型的方法,但也许它们可以自动向上转发到Double的/ Long,并且实际上没有正在运行的java8编译器atm。