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));