我有几天学习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"下
"解析为类名"显然与"评估解析为类名的东西"不同,所以我在这里尝试做的并不是点特殊形式所支持的.
(更新:我已经准备好了可以作为解决方案接受的东西......原来的答案仍然低于帖子末尾的横向规则.)
我刚刚写了一个宏来启用它:
(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静态方法导入为宏.
| 归档时间: |
|
| 查看次数: |
3688 次 |
| 最近记录: |