2015-06-07 62 views
5

我不明白,为什么下面的代码会产生一个反射警告:反思警告

(set! *warn-on-reflection* true) 

(defmacro my-macro [k] `(.length ~(with-meta k {:tag String}))) 

(defn my-fun1 [k] (my-macro k)) 
;; Reflection warning, /tmp/form-init2370243866132870536.clj:1:18 - reference to field length can't be resolved. 

使用macroexpand-1表明,生成的代码确实有typehint,如果我写的一样手动代码不使用宏,没有反射警告:

(set! *print-meta* true) 

(macroexpand-1 '(my-macro k)) 
;; (.length ^java.lang.String k) 

(defn my-fun2 [k] (.length ^String k)) 
;; All good, no reflection warning 

标杆函数示出了警告不只是红色鲱鱼,反射实际上发生在运行时:

(time (reduce + (map my-fun1 (repeat 1000000 "test")))) 
;; "Elapsed time: 3080.252792 msecs" 

(time (reduce + (map my-fun2 (repeat 1000000 "test")))) 
;; "Elapsed time: 275.204877 msecs" 
+0

当我运行'macroexpand-1'行,我没有看到类型暗示。然而,我认为宏观有问题,它不会接受字符串:'(my-macro“foo”); => java.lang.ClassCastException:java.lang.String不能转换为clojure.lang.IObj'。 – Mars

+0

你应该看到类型提示。你确定你把'* print-meta *'设置为true吗? – noziar

回答

3

该标签应该是一个符号,而不是一个类。所以,下面的代码工作:

(defmacro my-macro [k] `(.length ~(with-meta k {:tag `String}))) 

这在documentation of special forms实际上是说:

:标签

一个符号命名类或A类对象表示了Java 类型var中的对象的返回值,或者如果对象为fn的返回值。

macroexpand-1显示类型暗示,是无效的,但看起来完全像一个正确的事实是非常容易引起误解:)

+1

请注意,在'String'上使用反引号稍微正确一些,而不是普通的引用。对于java.lang中的类,它将是相同的,因为这些类是在所有名称空间中导入的,因此非限定引用可以工作,但是如果尝试使用像ArrayList这样的类,它将只能在导入了java.util.ArrayList的名称空间。 – amalloy

相关问题