генерировать Java-бобы с clojure - PullRequest
7 голосов
/ 17 марта 2012

Есть ли способ легко генерировать Java-бины, заданные вектором в clojure? Например, для заданного вектора:

[
    String :key1
    Integer :key2
]

Я бы хотел, чтобы он генерировал такой код:

public class NotSureWhatTheTypeWouldBeHere {
    private String key1;
    private Integer key2;

    public NotSureWhatTheTypeWouldBeHere() {}
    public NotSureWhatTheTypeWouldBeHere(String key1, Integer key2) {
        this.key1 = key1;
        this.key2 = key2;
    }

    public void setKey1(String key1) {
        this.key1 = key1;
    }
    public String getKey1() {
        return this.key1;
    }
    public void setKey2(Integer key2) {
        this.key2 = key2;
    }
    public String getKey2() {
        return this.key2;
    }

    // and equals,hashCode, toString, etc.
}

Для контекста я хотел бы написать приложение, написанное на java, но вызывающее библиотеку, написанную на clojure. Так что это означает, что возвращаемые значения должны быть Java-бинами (я знаю, что они не должны быть, но я бы хотел, чтобы они были). Одним из способов было бы определить модель в java, а затем использовать обычное взаимодействие java clojure для заполнения модели в коде clojure, но мне нравится идея краткого вектора (или карты) clojure, расширяющегося до (подробного) Java-бина.

Ответы [ 3 ]

3 голосов
/ 18 марта 2012

Я не думаю, что ваш Java-код будет хорошо работать с автоматически сгенерированными классами, совместимыми с Java-бином.Вам нужно , чтобы иметь хотя бы интерфейс на стороне Java, чтобы иметь представление о том, что Clojure собирается вернуть.Без этого вам придется вернуться к:

Object result = callClojureLib(params);

Тогда, независимо от того, реализует ли фактический результат контракт Java-бина, ваш Java-код должен будет выполнять все виды волшебства отражения, чтобы иметь возможностьвызвать сеттер, так как вам не хватает спецификации класса.

Еще один подход к проблеме - использование интерфейса java.util.Map в качестве контракта между мирами Java и Clojure.Таким образом, вы можете просто использовать простые карты Clojure в качестве объектов передачи, так как они назначаются на java.util.Map:

user=> (isa? (class {}) java.util.Map)
true
2 голосов
/ 18 мая 2012

Отнюдь не идеально, и, вероятно, у вас возникает много непредвиденных проблем, когда вы пытаетесь его использовать, но я думаю, вы можете начать с чего-то вроде:

  (ns genbean)

  (defn -init []
     [[] (atom {})])

  (defn -toString
    [this]
     (str @(.state this)))

  (defn -equals
    [this other]
    (= @(.state this) @(.state other)))

  (defn -hashCode
    [this]
    (hash @(.state this)))

  (defn set-field
     [this key value]
     (swap! (.state this) into {key value}))

  (defn get-field
     [this key]
     (@(.state this) key))

  (defn gen-method-defs [fields]
     (mapcat (fn [[name type]] [[(str "set" name) [type] 'void]
                          [(str "get" name) [] type]]) fields))

  (defn def-access-methods [fields]
     (mapcat (fn [field] [`(defgetter ~field) `(defsetter ~field)]) fields))

  (defmacro defsetter [field]
     `(defn ~(symbol (str "-set" field)) [this# value#]
         (set-field this# ~(keyword field) value#)))

  (defmacro defgetter [field]
     `(defn ~(symbol (str "-get" field))
        [this#]
        (get-field this# ~(keyword field))))

  (defmacro defbean [bean fields]
     `(do
         (gen-class
            :main false
            :state ~'state
            :init ~'init
            :name ~bean
            :methods ~(gen-method-defs fields))
         ~@(def-access-methods (keys fields))
         ))

  (defbean com.test.Foo {Bar Integer Baz String What int})

Использование со стороны Java:

  Foo f = new Foo();
  f.setBaz("hithere");
  f.setBar(12);
  System.out.println("f = " + f);
  Foo f2 = new Foo();
  System.out.println("f2.equals(f) = " + f2.equals(f));
  f2.setBaz("hithere");
  f2.setBar(12);
  System.out.println("f2.equals(f) = " + f2.equals(f));
  System.out.println("(f2.hashCode() == f.hashCode()) = " + (f2.hashCode() == f.hashCode()));

Производит:

f = {:Baz "hithere", :Bar 12}
f2.equals(f) = false
f2.equals(f) = true
(f2.hashCode() == f.hashCode()) = true

Обратите внимание, что вам нужно будет скомпилировать пространство имен geanbean. Реализация использует атом для хранения всех свойств, поэтому убедитесь, что вы понимаете компромиссы.

Кроме того, при работе в Clojure вы, вероятно, не хотите работать с javabeans, но вы можете создать несколько методов для получения и установки атома, удерживающего состояние.

0 голосов
/ 18 марта 2012

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

Такие ключи, как :name, являютсятипа clojure.lang.Keyword, а не String или Integer и т. д. (также вы обычно не объявляете типы в Clojure).Они часто используются в картах (которые используют синтаксис {}) для получения значений, например, следующий код извлекает значение, связанное с :key2, с карты {:key1 "hello", :key2 4}.

(get {:key1 "hello", :key2 4} :key2)
4

I'mне уверен, что ваш пример пытается сказать, что у вас :key1 связано с String значением и :key2 связано с Integer значением или если вы думаете :key1 имеет тип String.Если первый, возможно, захочет использовать карту вместо вектора.

Боюсь, я не думаю, что знаю достаточно о бинах Java или, в частности, о вашем случае использования, чтобы помочь гораздо дальше.

...