Clojure 中是否有针对 Java 函数的应用函数?

2022-09-03 06:24:03
 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函数?如果不是,我怎么能写一个宏或函数来支持这一点?


答案 1

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

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

答案 2

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

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

其次,类型系统之间的阻抗不匹配起作用。对于Java来说,要以Java的方式获得完全成熟的FP支持,它需要静态类型化。事实证明,以真正令人满意的方式做到这一点非常困难(有关正在使用并将在Java 8中到达的方法的详细信息,请参阅lambda-dev邮件列表上的讨论)。

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

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


推荐