我不明白为什么以下代码产生反射警告:
(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.
Run Code Online (Sandbox Code Playgroud)
使用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
Run Code Online (Sandbox Code Playgroud)
对函数进行基准测试表明警告不仅仅是一个红色的鲱鱼,反射实际上发生在运行时:
(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"
Run Code Online (Sandbox Code Playgroud)
标签应该是一个符号,而不是一个类。所以下面的代码有效:
(defmacro my-macro [k] `(.length ~(with-meta k {:tag `String})))
Run Code Online (Sandbox Code Playgroud)
这实际上在特殊表格的文档中有所说明:
:标签
命名类或 Class 对象的符号,指示 var 中对象的 Java 类型,或其返回值(如果对象是 fn)。
macroexpand-1显示无效但看起来与正确的类型提示完全相同的事实是相当具有误导性的:)