Java 8 方法参考:编译时的方法验证

2022-09-03 14:49:13

我想使用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){}

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

是否有可能以这样一种方式实现函数接口,即任何方法引用都是有效的参数,因此将在编译时进行验证?MyFunctionalInterface

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


答案 1

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

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


答案 2

您可以尝试类似下面的方法:

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

推荐