Clojure变量和Java静态方法

时间:2010-05-17 19:29:39

标签: 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)

从异常看起来,运行时期望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的医生说

  

如果第一个操作数是符号   解析为类名,访问权限   被认为是静态成员   命名类的......否则就是   被认为是实例成员

“The Dot special form”下的

http://clojure.org/java_interop

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

3 个答案:

答案 0 :(得分:6)

(更新:我已经准备了一些可以接受的解决方案......原来的答案仍然低于帖子末尾的水平规则。)


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

(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将静态方法导入为宏。

答案 1 :(得分:1)

您已成功将该类存储在jmod中,但isPrivate是java.lang.reflect.Modifier的静态方法,而不是java.lang.Class。

你可以用反射来做到这一点:

(. (. jmod getMethod "isPrivate" (into-array [Integer/TYPE])) 
  invoke nil (into-array [1]))

答案 2 :(得分:0)

这是一个受前两个答案启发的宏,它使用类名和对象上的实例方法处理类名和变量的静态方法:

(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)
相关问题