Clojure变量和Java静态方法

j-g*_*tus 6 clojure

我有几天学习Clojure并且有一些出牙问题,所以我在寻求建议.

我正在尝试将一个Java类存储在Clojure var中并调用其静态方法,但它不起作用.

例:

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)
Run Code Online (Sandbox Code Playgroud)

从异常看起来,运行时期望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在"The Dot special form"下

"解析为类名"显然与"评估解析为类名的东西"不同,所以我在这里尝试做的并不是点特殊形式所支持的.

Mic*_*zyk 6

(更新:我已经准备好了可以作为解决方案接受的东西......原来的答案仍然低于帖子末尾的横向规则.)


我刚刚写了一个宏来启用它:

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

我们的想法是创建一个单一用途的命名空间,将给定类的静态作为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#))))
Run Code Online (Sandbox Code Playgroud)

上面查找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#)))
Run Code Online (Sandbox Code Playgroud)

(原答案如下.)

我意识到这不是你要求的,但也许clojure.contrib.import-static/import-static对你有用:

(use 'clojure.contrib.import-static)

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

(isPrivate 1)
; => false
(isPrivate 2)
; => true
Run Code Online (Sandbox Code Playgroud)

请注意,将import-static静态方法导入为宏.