Как я вытягиваю 'статические заключительные' константы от класса Java в пространство имен Clojure?

Я пытаюсь перенести библиотеку 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.

Спасибо

7
задан Joe Holloway 3 April 2010 в 20:33
поделиться

3 ответа

К сожалению, макрос 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)))
3
ответ дан 7 December 2019 в 09:59
поделиться

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

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

1
ответ дан 7 December 2019 в 09:59
поделиться

(Теперь этот ответ включает два рабочих решения, одно основано на моей первоначальной идее с стажером , а второе - на предложении Данлея использовать 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 может дать вам правильные идеи, если мой эскиз выше окажется каким-то образом неверным.

2
ответ дан 7 December 2019 в 09:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: