10

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

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

我的问题是除了反射/转换为地图之外,还有哪些其他方法可以解决这个问题?

在我在这条道路上走得更远之前,我想知道是否有人知道我如何从 Rhino 中蚕食一些东西,或者可能是javax.script.*对这个概念进行了深思熟虑的实现。或者也许是我没有考虑过的完全不同的方法。

编辑:是的,我熟悉反射(我相信这是 Introspector 在幕后使用的)。我只是好奇是否还有其他经过深思熟虑的解决方案。

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

答:我将 Mike 的答案标记为最好的,因为它是一个最接近的完整概念。对于这种特殊情况,我可能不会走那条路,但这肯定是一种有用的方法。任何浏览此内容的人都应该确保阅读这里的对话,因为那里也有很多有用的信息。

谢谢!

4

3 回答 3

10

如果您知道要公开的 API 集,例如您知道要访问长度方法和迭代器方法,则可以定义一个接口:

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();
    }
  }
}
于 2010-12-02T00:39:15.763 回答
1

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

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

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

于 2010-12-02T14:56:28.047 回答
0

看看 java.lang.Class 的方法和反射 API:java.lang.reflect.*

于 2010-12-01T23:54:32.717 回答