简体   繁体   English

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

[英]Is there an apply function in Clojure for Java functions?

 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)

Is there any way to do apply for java functions in Clojure? 有没有办法在Clojure中申请java函数? If not how could I write a macro or function that would support this? 如果不是,我怎么能写一个支持这个的宏或函数?

The simplest thing I can think of is wrapping it in a function, but I'm not entirely sure if this is the best/most idiomatic way: 我能想到的最简单的事情是将它包装在一个函数中,但我不完全确定这是否是最佳/最惯用的方式:

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

Or, slightly shorter but equivalent: 或者,略短但相当:

user> (apply #(Integer/rotateRight %1 %2) [0 0])
0

Alternatively, you could create a proper wrapper function for your java method call: 或者,您可以为java方法调用创建一个正确的包装函数:

(defn rotate-right [a b]
  (Integer/rotateRight a b))

You'd use it like so: 你会这样使用它:

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

edit : just for fun, inspired by iradik's comment about efficieny, time comparisons between three different ways of calling this method: 编辑 :只是为了好玩,灵感来自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

A couple of points which, while not a direct answer, are relevant here. 有几点虽然不是直接的答案,但在这里是相关的。

First off, Java does not have functions. 首先,Java没有功能。 It only has either instance methods or static methods. 它只有实例方法或静态方法。 This may seem like a pedantic distinction, but it does make a difference (as shown in some of the other examples where different forms are needed for static and instance invocation). 这似乎是一种迂腐的区别,但它确实有所作为(如静态和实例调用需要不同形式的其他一些示例所示)。

Secondly, the impedance mismatch between the type systems comes into play. 其次,类型系统之间的阻抗不匹配发挥作用。 For Java to have fully-fledged FP support in a Javaish manner, it would need to be statically typed. 为了使Java以Javaish方式获得完全成熟的FP支持,它需要静态类型化。 This turns out to be quite hard to do in a truly satisfactory manner (see the discussion on the lambda-dev mailing list for details of the approach that is being used and will arrive in Java 8). 事实证明,以一种真正令人满意的方式很难做到(有关正在使用的方法的详细信息,请参阅lambda-dev邮件列表中的讨论,并将在Java 8中提供)。

From these two points, we can see that from Clojure the best we can really do is to support an "all bets are off" approach to calling Java methods via #() or similar. 从这两点来看,我们可以看到,从Clojure中我们真正做的最好的事情就是支持通过#()或类似方法调用Java方法的“所有赌注都关闭”的方法。 Clojure will only choose between forms to call based on the arity of the argument, so some sort of type hints or casting may be needed to ensure the correct overloaded Java method is called. Clojure只会根据参数的arity选择要调用的表单,因此可能需要某种类型的提示或转换来确保调用正确的重载Java方法。

More importantly, of course, if a user passes an argument of a type that Java isn't expecting, or can't handle, this may not be detectable until runtime. 更重要的是,当然,如果用户传递的Java类型的参数不是Java或者不能处理的,那么在运行时之前可能无法检测到这种情况。

I wrote a few macros to do this after being inspired by gertalot's answer. 在受到gertalot答案的启发后,我写了一些宏来做这件事。 Appears to compile to the equivalent normal code. 似乎编译为等效的普通代码。 Benchmark was identical. 基准是相同的。 Curious what you think. 好奇你的想法。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM