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

查看:19
本文介绍了Clojure 变量和 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 文档说

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 在点特殊形式"下

解析为类名"显然与对解析为类名的事物求值"不同,因此点特殊形式不支持我在这里尝试做的事情.

"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.

推荐答案

(更新:我已经准备了一些可能被接受的解决方案......原始答案在接近帖子.)

我刚刚编写了一个宏来启用此功能:

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

以上以一种有点复杂的方式查找包含 import-all-statics 宏的 Var(但是,如果宏在当前命名空间中可见,则保证可以工作).如果您知道它将在哪个命名空间中找到,那么我编写的原始版本是一个更简单的替代:

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

<小时>

(原始答案如下.)

我意识到这并不是您真正想要的,但也许 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 将静态方法作为宏导入.

Note that import-static imports static methods as macros.

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

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