用Java模拟鸭子打字

问题:我希望能够在Java中一般性地访问Java ojbect上的任何属性/字段,类似于动态语言(想想Groovy,JavaScript)的方式。 我不知道当时我正在编写这个管道代码它是什么类型的对象或属性/字段名称是什么。 但是当我去使用它时我会知道属性/字段名称。

我目前的解决方案:到目前为止,我已经编写了一个简单的包装类,它使用java.beans.Introspector来获取Bean / POJO的属性并将它们公开为Map 。 它很粗糙但适用于简单的情况。

我的问题是除了reflection/转换为Map之外还有哪些方法可以解决这个问题?

在我走得太远之前,我想知道是否有人知道我如何能够从Rhino或javax.script.*蚕食一些东西javax.script.*这个概念已经过深思熟虑。 或者也许是一种我没有考虑过的完全不同的方法。

编辑:是的我熟悉reflection(我相信Introspector无论如何都在使用它)。 如果有任何其他经过深思熟虑的解决方案,我只是好奇。

编辑2:似乎最流行的答案涉及1)直接或通过辅助类进行reflection,和/或2)映射到实现所需类成员的接口。 关于利用Groovy的评论,我真的很感兴趣。 由于Groovy具有真正的duck-typing并且它是一种JVM语言,有没有办法在Groovy中创建一个简单的帮助器并从Java调用它? 这将非常酷,可能更灵活,性能更好。

答:我认为迈克的答案是最好的,因为它是一个最接近的完整概念。 对于这种特殊情况,我可能不会采用这种方式,但它肯定是一种有用的方法。 任何看过这个的人都应该确保在这里阅读对话,因为那里有很多有用的信息。

谢谢!

如果您知道要公开的API集,假设您知道要访问length方法和迭代器方法,则可以定义接口:

 public interface TheInterfaceIWant { int length(); void quack(); } 

并且您希望能够使用此接口访问未实现此接口的实例上的相应方法,您可以使用代理类: 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); } }); 

然后你可以像使用动态类型语言中的鸭子一样使用包装器。

 if (aDuckWrapper.length() > 0) { aDuckWrapper.quack(); } 

这是一个全长的可运行示例,使用包装器打印“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(); } } } 

我刚刚遇到的另一种利用(滥用?)类型擦除的方法很有趣:

http://rickyclarkson.blogspot.com/2006/07/duck-typing-in-java-and-no-reflection.html

我不确定我是否购买这与直接使用接口有很大不同,但也许对其他人有用。

看一下java.lang.Class的方法和reflectionAPI:java.lang.reflect。*

Interesting Posts