调用存储在var中的Java类的静态方法



我正在处理一个事件流,编码为Google Protobuffers,存储为字节。Protobuffers的诀窍在于,你必须在加载一个东西之前知道它的类。另一个技巧是,我加载的事件是嵌套的。

。我有最内层事件的ByteArray。幸运的是,在最内层的事件中有一个字段指定了最内层事件的类型,因此我可以确定应该由哪个类加载它。

好消息:解析函数在每个候选类上具有相同的名称(parseFrom)。坏消息是:我需要调用的函数是静态的,并且根据数量和类型进行分派。

我要做的是:

(ns do-the-thing
  (import com.thing.place Type$Subtype Type$SecondSubType)
(def decl-obj-map
  {:type-subtype Type$SubType
   :type-second-subtype Type$Second$SubType})
(defn call-fn
  [class n-args method]
  (let [o (gensym)
        args (repeatedly n-args gensym)
        assure-symbol (fn [thing] (if (symbol? thing) thing (symbol thing)))
        method (assure-symbol method)]
    (eval
     `(fn [~o ~@args]
        (. ~(with-meta o {:tag class})
           (~method ~@args))))))
(def event-type (.getSubtypeField event-obj)
(def parse-func (call-fn (event-type decl-obj-map) 0 "parseFrom")
(parse-func (.getByteArrayFromInnerObj inner-obj))

。这根本行不通。代替call-fn,我也从clojure.contrib尝试了这个方法。它会抛出永远难以理解的IllegalArgumentException array element type mismatch java.lang.reflect.Array.set (Array.java:-2)错误。

有人知道吗?

这段代码在应用于类的静态方法时存在一些问题,最重要的是类必须在编译时为dot特殊形式和同类(注:(Class/method ...)扩展为(. Class method ...))所知。因此,使用此策略,您必须在每次调用时使用eval——必须将eval移到返回的函数中(在对其进行了一些修补之后)。这是不可取的。

你可以用反射来解决这个问题,例如

(defn call-fn [^Class class method] 
  (fn [& args] 
    (clojure.lang.Reflector/invokeStaticMethod 
      (.getName class) 
      (str method) 
      (to-array args))))

(def my-abs (call-fn Math "abs"))
(my-abs -1) ;=> 1
但是我认为你把问题设计得太过分了。考虑从规范到函数的映射,而不是从类型关键字到类的映射,例如
(def spec->parser
  {:type-subtype {1 #(Type$Subtype/parseFrom %) 2 #(Type$Subtype/parseFrom % %2)} 
   :type-second-subtype {1 #(Type$SecondSubtype/parseFrom %) ...}})

那么你的call-fn就是

(defn get-parser [type-kw nargs] (get-in spec->parser [type-kw nargs]))

或简写为#(get-in spec->parser &%)

相关内容

  • 没有找到相关文章

最新更新