Java中的类型安全方法reflection

是否有任何实用的方法以类型安全的方式引用类的方法? 一个基本的例子是,如果我想创建类似以下实用程序函数:

public Result validateField(Object data, String fieldName, ValidationOptions options) { ... } 

为了打电话,我必须这样做:

 validateField(data, "phoneNumber", options); 

这迫使我要么使用魔法字符串,要么用该字符串声明一个常量。

我很确定没有办法用库存Java语言解决这个问题,但是有某种(生产级)预编译器或替代编译器可以提供解决方法吗? (类似于AspectJ如何扩展Java语言)做以下事情会很好:

 public Result validateField(Object data, Method method, ValidationOptions options) { ... } 

并称之为:

 validateField(data, Person.phoneNumber.getter, options); 

正如其他人所说,没有真正的方法可以做到这一点……而且我没有看到支持它的预编译器。 至少可以说,语法很有趣。 即使在您的示例中,它也只能覆盖用户可能想要做的潜在reflection可能性的一小部分,因为它不会处理非标准访问器或带参数的方法等。

即使在编译时无法检查,如果您希望错误的代码尽快失败,那么一种方法是在类初始化时解析引用的Method对象。

想象一下,你有一个实用工具方法来查找可能抛出错误或运行时exception的Method对象:

 public static Method lookupMethod( Class c, String name, Class... args ) { // do the lookup or throw an unchecked exception of some kind with a really // good error message } 

然后在您的类中,使用常量来预先解析您将使用的方法:

 public class MyClass { private static final Method GET_PHONE_NUM = MyUtils.lookupMethod( PhoneNumber.class, "getPhoneNumber" ); .... public void someMethod() { validateField(data, GET_PHONE_NUM, options); } } 

至少在第一次加载MyClass时它会失败。

我经常使用reflection,特别是bean属性reflection,我刚刚习惯了运行时的exception。 但是由于各种其他原因,这种样式的bean代码往往会出错,而且非常动态。 对于介于两者之间的东西,上面会有所帮助。

语言中没有任何东西 – 但我相信Java 7的闭包提案的一部分包括方法文字。

我害怕,除此之外我没有任何建议。

Java错过了语法糖来做一些像Person.phoneNumber.getter一样好的Person.phoneNumber.getter 。 但是如果Person是一个接口,你可以使用动态代理记录getter方法。 您可以使用CGLib记录非最终类的方法,就像Mockito所做的那样。

 MethodSelector selector = new MethodSelector(Person.class); selector.select().getPhoneNumber(); validateField(data, selector.getMethod(), options); 

MethodSelector的代码: https : //gist.github.com/stijnvanbael/5965609

查看http://jodd.org/doc/methref.html 。 它使用Jodd代理库(Proxetta)来代理您的类型。 不确定它的性能特征,但确实提供了类型安全性。

例如:假设Str.class有方法.boo() ,并且您希望将其名称作为字符串"boo"

 Methref m = Methref.on(Str.class); // `.to()` returns a proxied instance of `Str` upon which you // can call `.boo()` Methods on this proxy are empty except when // you call them, the proxy stores the method's name. So doing this // gets the proxy to store the name `"boo"`. m.to().boo(); // You can bget the name of the method you called by using `.ref()`: m.ref(); // returns "boo" 

API比上面的示例更多: http : //jodd.org/api/jodd/methref/Methref.html

是否有任何实用的方法以类型安全的方式引用类的方法?

首先,reflection类型安全的。 它只是动态类型,而不是静态类型。

因此,假设您需要静态类型的等效reflection,理论上的答案是它是不可能的。 考虑一下:

 Method m; if (arbitraryFunction(obj)) { obj.getClass().getDeclaredMethod("foo", ...); } else { obj.getClass().getDeclaredMethod("bar", ...); } 

我们可以这样做,以便不会发生运行时类型exception吗? 通常为NO,因为这需要certificatearbitraryFunction(obj)终止。 (这相当于停止问题,这被certificate是无法解决的,并且使用最先进的定理certificate技术…… AFAIK是难以处理的。)

我认为这个路障块适用于任何可以将任意Java代码注入逻辑的方法,该逻辑用于从对象的类中reflection选择方法。

在我看来,目前唯一适度的实用方法是用生成和编译Java源代码的东西替换reflection代码。 如果在“运行”应用程序之前发生此过程,则您满足静态类型安全性的要求。


我更多地询问结果总是相同的reflection。 IE Person.class.getMethod("getPhoneNumber", null)将始终返回相同的方法,并且完全可以在编译时解析它。

如果在编译包含此代码的类之后,您更改 Person以删除getPhoneNumber方法会发生什么?

你可以确定你可以反思地解决getPhoneNumber的唯一方法是你是否可以以某种方式阻止 Person被改变。 但你不能用Java做到这一点。 类的运行时绑定是该语言的基本部分。

(对于记录,如果您为非reflection性调用的方法执行此操作,则在加载两个类时会出现某种类型的IncompatibleClassChangeError …)

受到模拟框架的启发,我们可以想出以下语法:

 validator.validateField(data, options).getPhoneNumber(); Result validationResult = validator.getResult(); 

诀窍是通用声明:

 class Validator { public  T validateField(T data, options) {...} } 

现在,方法的返回类型与数据对象的类型相同,您可以使用代码完成(和静态检查)来访问所有方法,包括getter方法。

作为缺点,代码读起来不是很直观,因为对getter的调用实际上并没有得到任何东西,而是指示validation器validation字段。

另一种可能的选择是注释数据类中的字段:

 class FooData { @Validate(new ValidationOptions(...)) private PhoneNumber phoneNumber; } 

然后打电话:

 FooData data; validator.validate(data); 

根据带注释的选项validation所有字段。

框架picklock允许您执行以下操作:

 class Data { private PhoneNumber phoneNumber; } interface OpenData { PhoneNumber getPhoneNumber(); //is mapped to the field phoneNumber } Object data = new Data(); PhoneNumber number = ObjectAccess .unlock(data) .features(OpenData.class) .getPhoneNumber(); 

这与setter和private方法类似。 当然,这只是reflection的包装器,但是在解锁时不会在调用时发生exception。 如果您在构建时需要它,您可以编写unit testing:

  assertThat(Data.class, providesFeaturesOf(OpenData.class));