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

时间:2014-06-29 03:42:03

标签: java reflection clojure

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

因此。我有最内层事件的ByteArray。幸运的是,在next-to-mostmost事件中有一个字段指定了最里面的事件的类型,所以我可以找出应该加载它的类。

好消息:parse函数在每个候选类上都有相同的名称(parseFrom)。 坏消息:我需要调用的函数是静态的,并调度arity和type。

我想做的是:

(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 this method而不是clojure.contrib。它抛出了永远难以理解的IllegalArgumentException array element type mismatch java.lang.reflect.Array.set (Array.java:-2)错误。

任何?

1 个答案:

答案 0 :(得分:3)

该代码存在一些问题,适用于类的静态方法,最重要的是必须在编译时为点特殊形式和朋友知道该类[注意:(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 &%)