我想使用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
,使任何方法引用成为有效参数,从而在编译时进行验证?
您认为在编译时验证方法是否存在的任何其他方式?
答案 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。