Clojure vars和Java静态方法 [英] Clojure vars and Java static methods

查看:241
本文介绍了Clojure vars和Java静态方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有几天在学习Clojure并且有一些开头的问题,所以我要求咨询。

I'm a few days into learning Clojure and are having some teething problems, so I'm asking for advice.

我试图存储Java类在Clojure var并调用其静态方法,但它不工作。

I'm trying to store a Java class in a Clojure var and call its static methods, but it doesn't work.

示例:

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 查找明显失败。

From the exception it looks like the runtime expects a var to hold an object, so it calls .getClass() to get the class and looks up the method using reflection. In this case the var already holds a class, so .getClass() returns java.lang.Class and the method lookup obviously fails.

有没有其他方法,除了写我自己的宏?

Is there some way around this, other than writing my own macro?

在一般情况下,我想在变量中有一个对象或一个类,并调用相应的方法 - 为静态方法例如方法。

In the general case I'd like to have either an object or a class in a varible and call the appropriate methods on it - duck typing for static methods as well as for instance methods.

在这个特殊情况下,我只想为 java.lang.reflect.Modifier ,如果你愿意的别名。我知道 import ,但是寻找更一般的东西,比如Clojure命名空间别名,但是Java类。有没有其他机制这样做?

In this specific case I'd just like a shorter name for java.lang.reflect.Modifier, an alias if you wish. I know about import, but looking for something more general, like the Clojure namespace alias but for Java classes. Are there other mechanisms for doing this?

编辑

也许我只是对这些调用约定感到困惑。我认为Lisp(和Clojure扩展)模型是评估所有参数,并调用列表中的第一个元素作为一个函数。

Maybe I'm just confused about the calling conventions here. I thought the Lisp (and by extension Clojure) model was to evaluate all arguments and call the first element in the list as a function.

在这种情况下(= jmod java.lang.reflect.Modifier)返回true,(。getName jmod) .getName java.lang.reflect.Modifier)都返回相同的字符串。

In this case (= jmod java.lang.reflect.Modifier) returns true, and (.getName jmod) and (.getName java.lang.reflect.Modifier) both return the same string.

所以变量和类名清楚地评价同样的东西,但它们仍然不能以同样的方式调用。这是怎么回事?

So the variable and the class name clearly evaluate to the same thing, but they still cannot be called in the same fashion. What's going on here?

编辑2

回答我的第二个问题在这里),Clojure doc说

Answering my second question (what is happening here), the Clojure doc says that


如果第一个操作数是一个符号,
解析为一个类名,访问
被认为是指定类的静态成员
...否则它是
,假定是一个实例成员

If the first operand is a symbol that resolves to a class name, the access is considered to be to a static member of the named class... Otherwise it is presumed to be an instance member

http://clojure.org/java_interop 在The Dot特殊形式

http://clojure.org/java_interop under "The Dot special form"

解析到类名显然不等于求解到某个类名的东西,所以我想做的

"Resolving to a class name" is apparently not the same as "evaluating to something that resolves to a class name", so what I am trying to do here is not supported by the dot special form.

推荐答案

(更新:我已经准备好了可以接受的东西作为解决方案...原始答案低于水平规则,直到帖子结束。)

(Update: I've prepared something which might be acceptable as a solution... The original answer remains below a horizontal rule towards the end of the post.)

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

I've just written a macro to enable this:

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

想法是创建一个单一用途的命名空间,将给定类的静态导入为Vars到该命名空间,然后将命名空间别名为一些有用的名称。复杂,但它的工作原理! : - )

The idea is to create a single-purpose namespace, import the statics of a given class as Vars into that namespace, then alias the namespace to some useful name. Convoluted, but it works! :-)

代码如下所示:

(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 宏(但是,如果宏可以从当前命名空间可见, )。如果你知道要找到的命名空间,我写的原始版本是一个更简单的替换:

The above looks up the Var holding the import-all-statics macro in a somewhat convoluted way (which is, however, guaranteed to work if the macro is visible from the current namespace). If you know which namespace it's going to be found in, the original version I've written is a simpler replacement:

(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#)))






(原始回答如下。)


(Original answer below.)

这不是你要求的,但是可能 clojure.contrib.import-static / import-static 对你有用:

I realise that this is not really what you're asking for, but perhaps clojure.contrib.import-static/import-static will be useful to you:

(use 'clojure.contrib.import-static)

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

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

请注意, import-static 方法作为宏。

这篇关于Clojure vars和Java静态方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆