Как вытащить константы `static final` из Java-класса в пространство имен Clojure? - PullRequest
7 голосов
/ 03 апреля 2010

Я пытаюсь обернуть библиотеку Java привязкой Clojure. Один конкретный класс в библиотеке Java определяет группу статических конечных констант, например:

class Foo {
    public static final int BAR = 0;
    public static final int SOME_CONSTANT = 1;
    ...
}

У меня была мысль, что я мог бы проверить класс и вывести эти константы в мое пространство имен Clojure без явного def определения каждого из них.

Например, вместо того, чтобы явно подключить его так:

(def foo-bar Foo/BAR)
(def foo-some-constant Foo/SOME_CONSTANT)

Я смог бы проверить класс Foo и динамически подключить foo-bar и foo-some-constant в моем пространстве имен Clojure при загрузке модуля.

Я вижу две причины для этого:

A) Автоматически извлекает новые константы по мере их добавления в класс Foo. Другими словами, мне не пришлось бы изменять свою оболочку Clojure в случае, если интерфейс Java добавил новую константу.

B) Я могу гарантировать, что константы следуют более соглашению о наименовании Clojure-esque

На самом деле я не продался, но это хороший вопрос, который нужно задать, чтобы расширить мои знания о взаимодействии Clojure / Java.

Спасибо

Ответы [ 3 ]

3 голосов
/ 04 апреля 2010

К сожалению, макрос clojure.contrib.import-static не позволяет импортировать все статические конечные поля.Вы должны предоставить список полей для импорта.

Этот макрос - идиоматическая оболочка для import-static :

(ns stackoverflow
  (:use clojure.contrib.import-static)
  (:import (java.lang.reflect Modifier)))

(defmacro import-static-fields
  "Imports all static final fields of the class as (private) symbols
  in the current namespace.

  Example: 
      user> (import-static-fields java.lang.Integer)
      #'user/TYPE
      user> MAX_VALUE
      2147483647

  Note: The class name must be fully qualified, even if it has already
  been imported."
  [class]
  (let [final-static-field? (fn [field]
                  (let [modifiers (.getModifiers field)]
                (and (Modifier/isStatic modifiers) (Modifier/isFinal modifiers))))
    static-fields (map #(.getName %)
               (filter
                final-static-field?
                (.. Class (forName (str class)) getFields)))]
    `(import-static ~class ~@static-fields)))
2 голосов
/ 03 апреля 2010

(Этот ответ теперь включает в себя два рабочих решения, одно из которых основано на моей первоначальной идее с intern, а другое - на предложении Данлея использовать c.c.import-static. Думаю, мне нужно будет это исправить чуть позже , но я не могу тратить на это больше времени ...)

Чтобы извлечь статические поля:

(filter #(bit-and java.lang.reflect.Modifier/STATIC (.getModifiers %))
        (.getFields YourClass))

Затем сопоставьте #(intern *ns* (str "a-prefix-" (.getName %)) (.get YourClass nil)) через эту последовательность, чтобы получить значение ... Обратите внимание, что этот бит не проверен и, в частности, я не уверен насчет этого nil в .get; Поэкспериментируйте с java.lang.Field и посмотрите, что работает с вашим классом.

Обновление 2:

Хорошо, на самом деле подход, основанный на intern, не так уж плох с точки зрения читаемости:

user> (map #(intern *ns* (symbol (str "integer-" (.getName %))) (.get % java.lang.Integer))
           (filter #(bit-and java.lang.reflect.Modifier/STATIC
                             (.getModifiers %))
                   (.getFields java.lang.Integer)))
(#'user/integer-MIN_VALUE #'user/integer-MAX_VALUE #'user/integer-TYPE #'user/integer-SIZE)
user> integer-MIN_VALUE
-2147483648
user> integer-MAX_VALUE
2147483647
user> integer-TYPE
int
user> integer-SIZE
32

Обновление: (оставляя первое обновление на месте в качестве альтернативного решения)

Сочетание знаний Данлея о clojure.contrib с вышеизложенным дает следующее:

user> (map #(eval `(import-static java.lang.Integer ~(symbol (.getName %))))
           (filter #(bit-and java.lang.reflect.Modifier/STATIC
                             (.getModifiers %))
                   (.getFields java.lang.Integer)))
(#'user/MIN_VALUE #'user/MAX_VALUE #'user/TYPE #'user/SIZE)
user> MIN_VALUE
-2147483648
user> MAX_VALUE
2147483647
user> TYPE
int
user> SIZE
32

Он использует eval ... ну и что, он вряд ли "убьет производительность" и на самом деле довольно читабелен, чего не может быть сложное выражение, использующее intern. (На самом деле это не так уж и плохо ...: -)) Если вы предпочитаете intern, то хотя бы реализация import-static может дать вам правильные идеи, если мой набросок выше превратится как-то неправильно.

1 голос
/ 03 апреля 2010

Я не пробовал, но, возможно, clojure.contrib.import-static сможет это сделать.

Только что проверил: вам придется называть методы / поля при использовании import-static, но я оставлю этот ответ здесь, потому что он может быть полезен для людей, ищущих похожие ответы.

...