java - 在 Java 中模拟鸭子类型(duck typing)

标签 java groovy rhino dynamic-languages duck-typing

问题:我希望能够在 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/

相关文章:

java - 我必须在 Web 应用程序中将文件存储在 MS SQL 2008 DB 中。您建议将其存储为 xml 字符串还是 blob?

java - 检索在匿名内部类回调中收到的方法的结果?

java - 在 ViewSwitcher 中切换不确定的 ProgressBar 和 Image

groovy - 在 Groovy 多行字符串中循环列表

jenkins - 如何修复 'hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: testFunc.call() '

java - Mozilla Rhino 如何使用 nodejs "fs"模块?

java - 找出调用我的方法

rhino - 在 Rhino 中运行 JSLinux

wso2 - 如何使用应用程序服务器功能解决 WSO2 Carbon 4.0.3 中的 "Packages is not defined"?

groovy - 如何使用 GrabResolver 在 groovysh 中使用远程存储库