在 Clojure 中使用 java.lang.invoke.MethodHandle
using java.lang.invoke.MethodHandle in clojure
我正在学习这里的教程:https://www.baeldung.com/java-method-handles
在 clojure 中,我有一个简单的例子:
(import (java.lang.invoke MethodHandles
MethodHandles$Lookup
MethodType
MethodHandle))
(defonce +lookup+ (MethodHandles/lookup))
(def ^MethodHandle concat-handle (.findVirtual +lookup+
String
"concat"
(MethodType/methodType String String)))
(.invokeExact concat-handle (into-array Object ["hello" "there"]))
这给出了一个错误:
Unhandled java.lang.invoke.WrongMethodTypeException
expected (String,String)String but found (Object[])Object
Invokers.java: 476 java.lang.invoke.Invokers/newWrongMethodTypeException
Invokers.java: 485 java.lang.invoke.Invokers/checkExactType
REPL: 26 hara.object.handle/eval17501
REPL: 26 hara.object.handle/eval17501
Compiler.java: 7062 clojure.lang.Compiler/eval
Compiler.java: 7025 clojure.lang.Compiler/eval
core.clj: 3206 clojure.core/eval
core.clj: 3202 clojure.core/eval
main.clj: 243 clojure.main/repl/read-eval-print/f
有没有办法让 invoke
工作?
您可以使用 .invokeWithArguments
,它将根据提供的参数计算出正确的元数:
(.invokeWithArguments concat-handle (object-array ["hello" "there"]))
=> "hellothere"
或者您可以使用 .invoke
,但您需要 MethodHandle.asSpreader
将可变参数正确应用到具有固定元数的 String.concat
:
(def ^MethodHandle other-handle
(.asSpreader
concat-handle
(Class/forName "[Ljava.lang.String;") ;; String[].class
2))
(.invoke other-handle (into-array String ["hello" "there"]))
=> "hellothere"
如果可能的话,我不确定如何使用 Clojure 中的 .invokeExact
来完成这项工作。
The symbolic type descriptor at the call site of invokeExact must exactly match this method handle's type. No conversions are allowed on arguments or return values.
This answer对.invoke
和.invokeExact
的限制有更多解释。
一些基于@TaylorWood 回答的有趣基准:
(with-out-str
(time (dotimes [i 1000000]
(.concat "hello" "there"))))
=> "\"Elapsed time: 8.542214 msecs\"\n"
(with-out-str
(def concat-fn (fn [a b] (.concat a b)))
(time (dotimes [i 1000000]
(concat-fn "hello" "there"))))
=> "\"Elapsed time: 3600.357352 msecs\"\n"
(with-out-str
(def concat-anno (fn [^String a b] (.concat a b)))
(time (dotimes [i 1000000]
(concat-anno "hello" "there"))))
=> "\"Elapsed time: 16.461237 msecs\"\n"
(with-out-str
(def concat-reflect (.? String "concat" :#))
(time (dotimes [i 1000000]
(concat-reflect "hello" "there"))))
=> "\"Elapsed time: 1804.522226 msecs\"\n"
(with-out-str
(def ^MethodHandle concat-handle
(.findVirtual +lookup+
String
"concat"
(MethodType/methodType String String)))
(time (dotimes [i 1000000]
(.invokeWithArguments concat-handle (into-array Object ["hello" "there"])))))
=> "\"Elapsed time: 1974.824815 msecs\"\n"
(with-out-str
(def ^MethodHandle concat-spread
(.asSpreader concat-handle
(Class/forName "[Ljava.lang.String;") ;; String[].class
2))
(time (dotimes [i 1000000]
(.invoke other-handle (into-array String ["hello" "there"])))))
=> "\"Elapsed time: 399.779913 msecs\"\n"
我正在学习这里的教程:https://www.baeldung.com/java-method-handles
在 clojure 中,我有一个简单的例子:
(import (java.lang.invoke MethodHandles
MethodHandles$Lookup
MethodType
MethodHandle))
(defonce +lookup+ (MethodHandles/lookup))
(def ^MethodHandle concat-handle (.findVirtual +lookup+
String
"concat"
(MethodType/methodType String String)))
(.invokeExact concat-handle (into-array Object ["hello" "there"]))
这给出了一个错误:
Unhandled java.lang.invoke.WrongMethodTypeException
expected (String,String)String but found (Object[])Object
Invokers.java: 476 java.lang.invoke.Invokers/newWrongMethodTypeException
Invokers.java: 485 java.lang.invoke.Invokers/checkExactType
REPL: 26 hara.object.handle/eval17501
REPL: 26 hara.object.handle/eval17501
Compiler.java: 7062 clojure.lang.Compiler/eval
Compiler.java: 7025 clojure.lang.Compiler/eval
core.clj: 3206 clojure.core/eval
core.clj: 3202 clojure.core/eval
main.clj: 243 clojure.main/repl/read-eval-print/f
有没有办法让 invoke
工作?
您可以使用 .invokeWithArguments
,它将根据提供的参数计算出正确的元数:
(.invokeWithArguments concat-handle (object-array ["hello" "there"]))
=> "hellothere"
或者您可以使用 .invoke
,但您需要 MethodHandle.asSpreader
将可变参数正确应用到具有固定元数的 String.concat
:
(def ^MethodHandle other-handle
(.asSpreader
concat-handle
(Class/forName "[Ljava.lang.String;") ;; String[].class
2))
(.invoke other-handle (into-array String ["hello" "there"]))
=> "hellothere"
如果可能的话,我不确定如何使用 Clojure 中的 .invokeExact
来完成这项工作。
The symbolic type descriptor at the call site of invokeExact must exactly match this method handle's type. No conversions are allowed on arguments or return values.
This answer对.invoke
和.invokeExact
的限制有更多解释。
一些基于@TaylorWood 回答的有趣基准:
(with-out-str
(time (dotimes [i 1000000]
(.concat "hello" "there"))))
=> "\"Elapsed time: 8.542214 msecs\"\n"
(with-out-str
(def concat-fn (fn [a b] (.concat a b)))
(time (dotimes [i 1000000]
(concat-fn "hello" "there"))))
=> "\"Elapsed time: 3600.357352 msecs\"\n"
(with-out-str
(def concat-anno (fn [^String a b] (.concat a b)))
(time (dotimes [i 1000000]
(concat-anno "hello" "there"))))
=> "\"Elapsed time: 16.461237 msecs\"\n"
(with-out-str
(def concat-reflect (.? String "concat" :#))
(time (dotimes [i 1000000]
(concat-reflect "hello" "there"))))
=> "\"Elapsed time: 1804.522226 msecs\"\n"
(with-out-str
(def ^MethodHandle concat-handle
(.findVirtual +lookup+
String
"concat"
(MethodType/methodType String String)))
(time (dotimes [i 1000000]
(.invokeWithArguments concat-handle (into-array Object ["hello" "there"])))))
=> "\"Elapsed time: 1974.824815 msecs\"\n"
(with-out-str
(def ^MethodHandle concat-spread
(.asSpreader concat-handle
(Class/forName "[Ljava.lang.String;") ;; String[].class
2))
(time (dotimes [i 1000000]
(.invoke other-handle (into-array String ["hello" "there"])))))
=> "\"Elapsed time: 399.779913 msecs\"\n"