Я пытаюсь перенести библиотеку 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 interop.
Спасибо
К сожалению, макрос 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)))
Я не пробовал, но, возможно, clojure.contrib.import-static справится.
Только что проверено: вам нужно будет назвать методы / поля при использовании import-static, но я оставлю этот ответ здесь, потому что он может быть полезен для людей, ищущих похожие ответы.
(Теперь этот ответ включает два рабочих решения, одно основано на моей первоначальной идее с стажером
, а второе - на предложении Данлея использовать ccimport- static
. Думаю, мне нужно очистить это чуть позже, но сейчас я не могу тратить на это больше времени ...)
Чтобы извлечь статические поля:
(filter #(bit-and java.lang.reflect.Modifier/STATIC (.getModifiers %))
(.getFields YourClass))
Затем map # (intern * ns * (str "a-prefix-" (.getName%)) (.get YourClass nil))
в этой последовательности для получения значения ... Обратите внимание, что этот бит не тестировался и, в частности, я не уверен в том, что nil
в .get
; поэкспериментируйте с java.lang.Field
и посмотрите, что работает с вашим классом.
Обновление 2:
Хорошо, на самом деле подход, основанный на стажёре
, не так уж плох с точки зрения читабельности:
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
Обновление: (оставив первое обновление в качестве альтернативного решения)
Объединение знаний Данлея о закрытии.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
, по крайней мере, реализация import-static
может дать вам правильные идеи, если мой эскиз выше окажется каким-то образом неверным.