我正在处理以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)
错误。
任何?
答案 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 &%)