Имитация утки на Java - PullRequest
       45

Имитация утки на Java

10 голосов
/ 02 декабря 2010

Проблема: Я хотел бы иметь возможность общего доступа к в Java к любому свойству / полю в объекте Java, аналогично динамическому языку (например, Groovy, JavaScript)было бы.В то время, когда я пишу этот код, я не буду знать, какой это тип объекта или каким будет имя свойства / поля.Но я буду знать имя свойства / поля, когда буду его использовать.

Мое текущее решение: До сих пор я написал простой класс-обертку, который использует java.beans.Introspector для захватасвойства Боба / POJO и выставить их как Map<String, Object>.Это грубо, но работает для простых случаев.

Мой вопрос Какие есть другие методологии для решения этой проблемы, кроме отражения / преобразования в карту?

Прежде чем я тоже пойдугораздо дальше по этому пути, я хотел бы знать, знает ли кто-нибудь, как я мог бы что-то каннибализировать из Rhino или, возможно, javax.script.*, который имеет хорошо продуманную реализацию этой концепции.Или, может быть, совершенно другой подход, который я не рассматривал.

Редактировать: Да, я знаком с отражением (которое, я считаю, в любом случае использует Introspector под капотом).Мне было просто любопытно, были ли какие-то другие хорошо продуманные решения.

Редактировать 2: Похоже, что наиболее популярные ответы включают в себя 1) рефлексию либо напрямую, либо с помощью вспомогательных классов, и / или2) сопоставление с интерфейсами, которые реализуют желаемые члены класса.Я действительно заинтригован комментарием, в котором говорится об использовании Groovy.Так как Groovy имеет настоящую типизацию утилит и это язык JVM, есть ли способ сделать простой помощник в Groovy и вызывать его из Java?Это было бы действительно круто и, вероятно, более гибко и работало бы лучше.

Ответ: Я отметил ответ Майка как лучший, поскольку это полная концепция, которая подходит ближе всего.Я, вероятно, не буду идти по этому пути для этого конкретного случая, но это, безусловно, полезный подход.Любой, кто просматривает это, должен прочитать здесь все разговоры, так как там много полезной информации.

Спасибо!

Ответы [ 3 ]

10 голосов
/ 02 декабря 2010

Если вам известен набор 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();
}

Вот полный пример выполнения, который печатает «Кряк» четыре раза, используя обертку:

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();
    }
  }
}
1 голос
/ 02 декабря 2010

Другой метод, с которым я только что столкнулся, который использует (злоупотребляет?) Стирание типа, довольно интересен:

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

Я не уверен, что покупаю, что это оченьотличается от простого использования интерфейсов напрямую, но, возможно, это полезно для кого-то другого.

0 голосов
/ 02 декабря 2010

Посмотрите методы java.lang.Class и API отражения: java.lang.reflect. *

...