Имитация утиного набора текста на Java

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

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

У меня вопрос: какие еще существуют методы решения этой проблемы, помимо отражения / преобразования в карту?

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

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

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

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

Спасибо!


person mckamey    schedule 01.12.2010    source источник
comment
это только академическое упражнение?   -  person hvgotcodes    schedule 02.12.2010
comment
Вы знаете Java-отражение? Он позволяет вам получить доступ к любому полю и любому методу через имена, но это довольно сложно, если вы хотите использовать его для каждой команды в вашей программе.   -  person gigadot    schedule 02.12.2010
comment
Отражение - это правильный путь, особенно если вы не знаете, что вызывать до времени выполнения.   -  person OscarRyz    schedule 02.12.2010
comment
Что, если бы я знал имя свойства во время компиляции? Это вариант проблемы.   -  person mckamey    schedule 02.12.2010
comment
Как насчет самих JRuby или Groovy и т. Д.? Т.е. использовать их как прокси.   -  person    schedule 02.12.2010
comment
@pst Звучит интересно. Можете ли вы уточнить пример кода Groovy? Может быть, в качестве ответа?   -  person mckamey    schedule 02.12.2010
comment
@McKAMEY: если вам это нужно во время компиляции, вы можете сгенерировать код для удовлетворения заданного интерфейса (и вы даже можете создать интерфейс), который больше похож на структурную типизацию (это то, что я делаю)   -  person OscarRyz    schedule 02.12.2010
comment
@McKAMEY, вы знакомы с JSR 223, который добавляет поддержку языка сценариев в JVM? groovy.codehaus.org/JSR+223+Scripting+with+Groovy Показывает, как это можно сделать с помощью groovy. В качестве альтернативы, я думаю, Groovyc теперь поддерживает компиляцию смешанного приложения java / groovy: groovyland.wordpress.com/2007/06/06/   -  person Mike Samuel    schedule 02.12.2010
comment
@McKAMEY О вашем редактировании: Groovy et at использовать отражение. Даже scala для структурного типа (codemonkeyism.com/scala-goodness-structural-typing ) использовать отражение   -  person OscarRyz    schedule 02.12.2010
comment
@ Майк-Самуэль, именно это меня изначально и привело меня на этот путь. Я бы предпочел, чтобы он не разбирал скрипт из строк, но я подумал, что, вероятно, существуют классы поддержки. Мне было интересно, могу ли я использовать для этого некоторые из классов javax.script.* и заставить их работать.   -  person mckamey    schedule 02.12.2010
comment
@OscarRyz Интересно. Я заметил это в исходном коде Groovy. Они действительно широко используют кеширование, чтобы уменьшить проблемы с производительностью отражения: svn.codehaus.org/groovy/trunk/groovy/groovy-core/src/main/org/   -  person mckamey    schedule 02.12.2010
comment
@McKAMEY: Что ты вообще делаешь? Выглядит очень интересно и, вероятно, как-то связано с тем, над чем я работаю.   -  person OscarRyz    schedule 02.12.2010
comment
@OscarRyz завершает работу над кодом привязки в моем двустороннем движке шаблонов duelengine.org   -  person mckamey    schedule 02.12.2010
comment
@McKAMEY Вау !, выглядит очень интересно, посмотрю на следующей неделе. Итак, если вы собираетесь генерировать код на лету, я бы предложил сгенерировать интерфейс и класс, реализующий интерфейс, для имитации этого утиного набора текста.   -  person OscarRyz    schedule 02.12.2010


Ответы (3)


Если вы знаете набор 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();
    }
  }
}
person Mike Samuel    schedule 02.12.2010
comment
Интересный. Таким образом, в конечном итоге производительность будет примерно одинаковой между прокси и отражением / инспектором, поскольку все они используют отражение. Но это могло бы быть приятно для нас как переходника типа от одного интерфейса к другому. Спасибо! - person mckamey; 02.12.2010
comment
@McKAMEY, Ага. К сожалению, накладных расходов немного больше. Method.getParameterTypes() требует выделения массива и копирования для всех методов без параметров. Если это окажется значительными накладными расходами, некоторые умелые запоминания должны помочь уменьшить их, если у вас есть некоторые данные о шаблонах использования. - person Mike Samuel; 02.12.2010
comment
Самая большая проблема с этой техникой (помимо накладных расходов) заключается в том, что вы все равно должны поддерживать все это с помощью интерфейсов. Он динамичен в том смысле, что классы, которые вы принуждаете, не должны наследовать от этих интерфейсов, но код, который его использует, требует интерфейса. Это самый большой отход от ИМО по-настоящему утиной печати. Отлично работает как адаптер, когда у вас есть интерфейс и класс, который его не реализует. Вы даже можете переназначить имена методов, прежде чем искать реальный метод. - person mckamey; 02.12.2010
comment
Тип утки предназначен для языков с динамической типизацией, для языков со статической типизацией вам следует обратить внимание на структурную типизацию. Есть реализация, которая делает это (whiteoak.sourceforge.net). Однако Java не поддерживает структурную типизацию. - person OscarRyz; 02.12.2010
comment
@McKAMEY, я согласен, что это может быть не самое удобное решение ‹nit-picking›, но это утиный ввод. IIRC, Duck typing - это когда вы определяете контракт для элемента программы в терминах предоставленных операторов, а не в терминах номинальных типов или RTTI. То, что нужно объединить операторы в интерфейс и пройти этап упаковки, ортогонален вопросу о том, является ли это утиным набором текста. ‹/Nit-picking› - person Mike Samuel; 02.12.2010

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

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

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

person Community    schedule 02.12.2010
comment
В связи с этим: stackoverflow.com/questions/4288942/ - person OscarRyz; 02.12.2010

Взгляните на методы java.lang.Class и API отражения: java.lang.reflect. *

person Thomas    schedule 01.12.2010