Clojure Вар и статические методы Java

Я - несколько дней в изучение Clojure и имею некоторые проблемы роста, таким образом, я прошу совет.

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

Пример:

user=> (. java.lang.reflect.Modifier isPrivate 1)
false
user=> (def jmod java.lang.reflect.Modifier)
#'user/jmod
user=> (. jmod isPrivate 1)
java.lang.IllegalArgumentException: No matching method found: isPrivate for class java.lang.Class (NO_SOURCE_FILE:0)
    at clojure.lang.Compiler.eval(Compiler.java:4543)

От исключения похоже, что время выполнения ожидает, что var будет содержать объект, таким образом, это назовет .getClass () для получения класса и ищет метод с помощью отражения. В этом случае var уже содержит класс, таким образом, .getClass() возвраты java.lang.Class и поиск метода, очевидно, перестал работать.

Есть ли некоторый путь вокруг этого кроме записи моего собственного макроса?

В общем случае я хотел бы иметь или объект или класс в varible и назвать соответствующие методы на нем - утиный ввод для статических методов, а также например методов.

В этом конкретном случае я был бы точно так же, как более короткое название java.lang.reflect.Modifier, псевдоним, если Вы желаете. Я знаю о import, но ища что-то более общее, как псевдоним пространства имен Clojure, но для классов Java. Есть ли другие механизмы для того, чтобы сделать это?

Править:

Возможно, я просто смущен соглашениями о вызовах здесь. Я думал Lisp (и следовательно Clojure), модель должна была оценить все аргументы и назвать первый элемент в списке как функция.

В этом случае (= jmod java.lang.reflect.Modifier) возвращает true, и (.getName jmod) и (.getName java.lang.reflect.Modifier) оба возвращают ту же строку.

Таким образом, переменная и имя класса ясно оценивают к тому же самому, но их все еще нельзя назвать тем же способом. Что продолжается здесь?

Редактирование 2

Отвечая на мой второй вопрос (что происходит здесь), в документе Clojure говорится это

Если первый операнд является символом, который решает к имени класса, доступ считается статическому члену именованного класса... Иначе это, как предполагают, член экземпляра

http://clojure.org/java_interop под "Точечной специальной формой"

"Разрешение к имени класса" является, по-видимому, не тем же как "оценивающий к чему-то, что разрешает к имени класса", поэтому что я пытаюсь сделать, здесь не поддерживается точечной специальной формой.

6
задан j-g-faustus 17 May 2010 в 21:47
поделиться

3 ответа

(Обновление: я подготовил кое-что, что может быть приемлемым решением... Оригинальный ответ остался под горизонтальным правилом в конце сообщения.)


Я только что написал макрос для этого:

(adapter-ns java.lang.reflect.Modifier jmod)
; => nil
(jmod/isStatic 1)
; => false
(jmod/isStatic 8)
; => true

Идея состоит в том, чтобы создать одноцелевое пространство имен, импортировать статику данного класса как Vars в это пространство имен, а затем присвоить пространству имен какое-нибудь полезное имя. Запутанно, но работает! :-)

Код выглядит так:

(defmacro import-all-statics
  "code stolen from clojure.contrib.import-static/import-static"
  [c]
  (let [the-class (. Class forName (str c))
        static? (fn [x]
                  (. java.lang.reflect.Modifier
                     (isStatic (. x (getModifiers)))))
        statics (fn [array]
                  (set (map (memfn getName)
                            (filter static? array))))
        all-fields (statics (. the-class (getFields)))
        all-methods (statics (. the-class (getMethods)))
        import-field (fn [name]
                       (list 'def (symbol name)
                             (list '. c (symbol name))))
        import-method (fn [name]
                        (list 'defmacro (symbol name)
                              '[& args]
                              (list 'list ''. (list 'quote c)
                                    (list 'apply 'list
                                          (list 'quote (symbol name))
                                          'args))))]
    `(do ~@(map import-field all-fields)
         ~@(map import-method all-methods))))

(defmacro adapter-ns [c n]
  (let [ias (symbol (-> (resolve 'import-all-statics) .ns .name name)
                    "import-all-statics")]
    `(let [ns-sym# (gensym (str "adapter_" ~n))]
       (create-ns 'ns-sym#)
       (with-ns 'ns-sym#
         (clojure.core/refer-clojure)
         (~ias ~c))
       (alias '~n 'ns-sym#))))

Приведенный выше код ищет Var, содержащий макрос import-all-statics, несколько запутанным способом (который, однако, гарантированно работает, если макрос виден из текущего пространства имен). Если вы знаете, в каком пространстве имен он будет найден, оригинальная версия, которую я написал, является более простой заменой:

(defmacro adapter-ns [c n]
  `(let [ns-sym# (gensym (str "adapter_" ~n))]
     (create-ns 'ns-sym#)
     (with-ns 'ns-sym#
       (clojure.core/refer-clojure)
       ;; NB. the "user" namespace is mentioned below;
       ;; change as appropriate
       (user/import-all-statics ~c))
     (alias '~n 'ns-sym#)))

(Оригинальный ответ ниже.)

Я понимаю, что это не совсем то, что вы просите, но, возможно, clojure.contrib.import-static/import-static будет вам полезен:

(use 'clojure.contrib.import-static)

(import-static clojure.lang.reflect.Modifier isPrivate)

(isPrivate 1)
; => false
(isPrivate 2)
; => true

Обратите внимание, что import-static импортирует статические методы как макросы.

6
ответ дан 17 December 2019 в 00:05
поделиться

Вот макрос, вдохновленный двумя предыдущими ответами, который обрабатывает статические методы на именах классов и переменных с именами классов, а также методы экземпляров на объектах:

(defmacro jcall [obj & args]
  (let [ref (if (and (symbol? obj) 
                  (instance? Class (eval obj)))
              (eval obj)
              obj) ]
    `(. ~ref ~@args)))

Для меня, как относительного новичка в макросах, сложной частью было получение правильного порядка оценки.

Для других новичков: Параметр obj в макрос передается без оценки, и нам нужно принудительно оценить vars, чтобы имя var расширилось до имени класса, который он содержит. Для этого нам нужен явный eval, вне тела макроса.

Тест на то, является ли obj символом, существует для того, чтобы ограничить оценку переменными. Проверка на то, содержит ли переменная класс, нужна для того, чтобы пропустить оценку неклассов, тогда она работает и для объектов и методов экземпляра.

Пример использования:

   ;; explicit class name, static method
user=> (jcall java.lang.reflect.Modifier isPrivate 1) 
false
  ;; class name from var, static method
user=> (jcall jmod isPrivate 1) 
false

  ;; works for objects and instance methods too
user=> (jcall (Object.) toString) 
"java.lang.Object@3acca07b"
 ;; even with the object in a variable
user=> (def myobj (Object.))
#'user/myobj
user=> (jcall myobj toString)
"java.lang.Object@4ccbb612"

  ;; but not for instance methods on classes
user=> (jcall Object toString) 
java.lang.NoSuchFieldException: toString (NO_SOURCE_FILE:747)
0
ответ дан 17 December 2019 в 00:05
поделиться

Вы успешно сохраняете класс в jmod, но isPrivate является статическим методом java.lang.reflect.Modifier, а не java.lang.Class.

Вы можете сделать это с помощью отражения:

(. (. jmod getMethod "isPrivate" (into-array [Integer/TYPE])) 
  invoke nil (into-array [1]))
1
ответ дан 17 December 2019 в 00:05
поделиться
Другие вопросы по тегам:

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