问题:我希望能够在 Java 中一般地访问 Java ojbect 上的任何属性/字段,类似于动态语言(想想 Groovy, JavaScript) 会。我在编写此管道代码时不知道它是什么类型的对象或属性/字段名称是什么。但是当我去使用它时我会知道属性/字段名称。
我当前的解决方案:到目前为止,我已经编写了一个使用 java.beans.Introspector
的简单包装类获取 Bean/POJO 的属性并将它们公开为 Map<String, Object>
.它很粗糙,但适用于简单的情况。
我的问题是,除了反射/转换为 Map 之外,还有哪些其他方法可以解决这个问题?
在我走得更远之前,我想知道是否有人知道我如何从 Rhino 或 javax.script.*
中蚕食某些东西。它对这个概念进行了深思熟虑的实现。或者也许是我没有考虑过的完全不同的方法。
编辑: 是的,我很熟悉反射(我相信 Introspector 无论如何都在使用反射)。我只是好奇是否还有其他经过深思熟虑的解决方案。
编辑 2: 似乎最流行的答案涉及 1) 直接反射或通过辅助类反射,和/或 2) 映射到实现所需类成员的接口(interface)。我对谈论利用 Groovy 的评论非常感兴趣。由于 Groovy 具有真正的鸭子类型(duck typing)并且它是一种 JVM 语言,有没有办法在 Groovy 中制作一个简单的帮助器并从 Java 调用它?这真的很酷,而且可能更灵活,性能更好。
答案:我将 Mike 的答案标记为最佳答案,因为它是最接近的完整概念。对于这种特殊情况,我可能不会走那条路,但这肯定是一种有用的方法。浏览本文的任何人都应该确保阅读此处的对话,因为其中也有很多有用的信息。
谢谢!
最佳答案
如果您知道要公开的 API 集,比如说您知道要访问长度方法和迭代器方法,则可以定义一个接口(interface):
public interface TheInterfaceIWant {
int length();
void quack();
}
并且您希望能够使用此接口(interface)访问未实现此接口(interface)的实例上的相应方法,您可以使用代理类:http://download.oracle.com/javase/1.4.2/docs/api/java/lang/reflect/Proxy.html
所以你创建了一个代理
final Object aDuck = ...;
TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class[] { TheInterfaceIWant.class },
new InvocationHandler() {
public Object invoke(
Object proxy, Method method, Object[] args)
throws Throwable {
return aDuck.getClass().getMethod(
method.getName(), method.getParameterTypes()).invoke(aDuck, args);
}
});
然后您可以像在动态类型语言中使用鸭子类型(duck typing)一样使用包装器。
if (aDuckWrapper.length() > 0) {
aDuckWrapper.quack();
}
这是一个完整的可运行示例,它使用包装器打印四次“嘎嘎”:
import java.lang.reflect.*;
public class Duck {
// The interface we use to access the duck typed object.
public interface TheInterfaceIWant {
int length();
void quack();
}
// The underlying instance that does not implement TheInterfaceIWant!
static final class Foo {
public int length() { return 4; }
public void quack() { System.out.println("Quack"); }
}
public static void main(String[] args) throws Exception {
// Create an instance but cast away all useful type info.
final Object aDuck = new Foo();
TheInterfaceIWant aDuckWrapper = (TheInterfaceIWant) Proxy.newProxyInstance(
TheInterfaceIWant.class.getClassLoader(),
new Class[] { TheInterfaceIWant.class },
new InvocationHandler() {
public Object invoke(
Object proxy, Method method, Object[] args)
throws Throwable {
return aDuck.getClass().getMethod(
method.getName(), method.getParameterTypes()).invoke(aDuck, args);
}
});
for (int n = aDuckWrapper.length(); --n >= 0;) {
// Calling aDuck.quack() here would be invalid since its an Object.
aDuckWrapper.quack();
}
}
}
关于java - 在 Java 中模拟鸭子类型(duck typing),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4330330/