Clojure中是否有适用于Java函数的apply函数?


 user=> (Integer/rotateRight 0 0)
 0
 user=>  (apply Integer/rotateRight [0 0])
 CompilerException java.lang.RuntimeException: Unable to find static field: 
   rotateRight in class java.lang.Integer, compiling:(NO_SOURCE_PATH:172)

是否有办法在Clojure中应用java函数?如果不是,我怎么能写一个宏或函数,将支持这一点?

我能想到的最简单的事情就是把它包装在一个函数中,但我不完全确定这是否是最好/最习惯的方式:

user> (apply (fn [a b] (Integer/rotateRight a b)) [0 0])
0

或者,稍短但等效:

user> (apply #(Integer/rotateRight %1 %2) [0 0])
0
或者,您可以为您的java方法调用创建一个合适的包装器函数:
(defn rotate-right [a b]
  (Integer/rotateRight a b))

你可以这样使用:

user> (apply rotate-right [0 0])
0

edit:只是为了好玩,灵感来自于iradik关于三种不同调用方法之间的效率,时间比较的评论:

;; direct method call (x 1 million)
user> (time (dorun (repeatedly 1E6 #(Integer/rotateRight 2 3))))
"Elapsed time: 441.326 msecs"
nil
;; method call inside function (x 1 million)
user> (time (dorun (repeatedly 1E6 #((fn [a b] (Integer/rotateRight a b)) 2 3))))
"Elapsed time: 451.749 msecs"
nil
;; method call in function using apply (x 1 million)
user> (time (dorun (repeatedly 1E6 #(apply (fn [a b] (Integer/rotateRight a b)) [2 3]))))
"Elapsed time: 609.556 msecs"
nil

虽然不是直接回答,但有几点是相关的。

首先,Java没有函数。它只有实例方法或静态方法。这似乎是一种迂腐的区别,但它确实有区别(如静态调用和实例调用需要不同形式的其他一些示例所示)。

其次,类型系统之间的阻抗不匹配开始起作用。为了使Java以Java的方式具有完全成熟的FP支持,它需要是静态类型的。事实证明,以一种真正令人满意的方式做到这一点是相当困难的(请参阅lambda-dev邮件列表中的讨论,了解正在使用的方法的详细信息,并将在Java 8中出现)。

从这两点,我们可以看到,从Clojure中,我们真正能做的最好的事情就是支持通过#()或类似的方式调用Java方法的"一切皆有可能"的方法。Clojure只会根据参数的大小选择要调用的表单,因此可能需要某种类型提示或强制类型转换来确保调用正确的重载Java方法。

当然,更重要的是,如果用户传递的参数类型是Java不期望的,或者无法处理的,则可能要到运行时才能检测到。

在受到gertalot的回答的启发后,我编写了几个宏来完成这个任务。似乎编译为等效的正常代码。Benchmark也是一样。想知道你是怎么想的

(defmacro java-new-apply 
   ([klass] `(new ~klass))
   ([klass args] `(new ~klass ~@(map eval args))))
(defmacro java-static-apply 
   ([f] f)
   ([f args] `(~f ~@(map eval args))))
(defmacro java-method-apply 
   ([method obj] method obj) 
   ([method obj args] `(~method ~obj ~@(map eval args))))
;; get date for Jan 1 1969
(import java.util.Date)
(java-new-apply Date [69 1 1])
(macroexpand '(java-new-apply Date [69 1 1]))
(new Date 69 1 1)
(java-static-apply Integer/rotateRight [2 3])
(macroexpand '(java-static-apply Integer/rotateRight [2 3]))
(. Integer rotateRight 2 3)
(java-method-apply .substring "hello world!" [6 11])
(macroexpand '(java-method-apply .substring "hello world!" [6 11]))
(. "hello world!" substring 6 11)

最新更新