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

时间:2011-12-29 09:37:53

标签: java clojure

 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函数?如果不是,我怎么能写一个支持这个的宏或函数?

3 个答案:

答案 0 :(得分:17)

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

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

编辑:只是为了好玩,受到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

答案 1 :(得分:4)

有几点虽然不是直接的答案,但在这里是相关的。

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

其次,类型系统之间的阻抗不匹配发挥作用。为了使Java以Javaish方式获得完全成熟的FP支持,它需要静态类型化。事实证明这很难以一种真正令人满意的方式进行(请参阅lambda-dev邮件列表上的讨论,了解正在使用的方法的详细信息,并将在Java 8中提供)。

从这两点,我们可以看到,从Clojure中我们真正做的最好的事情是支持通过#()或类似方法调用Java方法的“所有赌注都关闭”的方法。 Clojure只会根据参数的arity选择要调用的表单,因此可能需要某种类型的提示或转换来确保调用正确的重载Java方法。

更重要的是,当然,如果用户传递了一个Java不期望或无法处理的类型的参数,那么在运行时之前可能无法检测到这种情况。

答案 2 :(得分:0)

在受到gertalot答案的启发后,我写了一些宏来做这件事。似乎编译为等效的普通代码。基准是相同的。好奇你的想法。

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