используя java.lang.invoke.MethodHandle в clojure - PullRequest
0 голосов
/ 18 сентября 2018

Я следую учебному пособию здесь: 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 работать?

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

Некоторые интересные тесты, основанные на ответе @ 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"
0 голосов
/ 18 сентября 2018

Вы можете использовать .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"

Я не уверен, как заставить это работать с .invokeExact из Clojure, если это возможно.

дескриптор символьного типа на сайте вызова invokeExact должен точно соответствовать типу этого дескриптора метода.Не допускается преобразование аргументов или возвращаемых значений.

В этом ответе содержится более подробное объяснение ограничений .invoke и .invokeExact.

...