【问题标题】:using java.lang.invoke.MethodHandle in clojure在 clojure 中使用 java.lang.invoke.MethodHandle
【发布时间】:2019-02-21 23:43:26
【问题描述】:

我在这里学习教程: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 工作?

【问题讨论】:

    标签: clojure methodhandle


    【解决方案1】:

    您可以使用 .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 进行这项工作。

    invokeExact 调用点的符号类型描述符必须与此方法句柄的类型完全匹配。不允许对参数或返回值进行转换。

    This answer.invoke.invokeExact的限制有更多解释。

    【讨论】:

    • 太棒了。感谢那。您知道使用invokeWithArguments 是否会导致某种性能损失?
    • @zcaudate 我会假设一些开销,并且看起来像你的基准 reflect ;) 那个。
    【解决方案2】:

    一些基于@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"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-23
      • 1970-01-01
      • 1970-01-01
      • 2014-10-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多