ryoakg
6/29/2016 - 3:09 PM

引数は同じで、コンストラクタを可変にしたい場合

引数は同じで、コンストラクタを可変にしたい場合

(defn f1 [c args] (eval `(new ~c ~@args)))
(defn f2 [c args] (apply-ctor c args))

(time (dotimes [i 10000] (f1 String [(str "hoge" i)])))
;; "Elapsed time: 4497.773954 msecs"

(time (dotimes [i 10000] (f2 String [(str "hoge" i)])))
;; "Elapsed time: 461.229971 msecs"
(ns apply-ctor
  (import [java.lang.reflect Constructor]))

(defn- acceptable-types? [ptypes atypes]
  (and (= (count ptypes) (count atypes))
       (every? (fn [[ptype atype]]
                 (or (= ptype atype)
                     ((ancestors atype) ptype)))
               (map vector ptypes atypes))))

(defn apply-ctor [^Class klass args]
  (let [atypes (into-array Class (map class args))
        ctors (for [^Constructor ctor (.getConstructors klass)
                    :let [ptypes (.getParameterTypes ctor)]
                    :when (acceptable-types? ptypes atypes)]
                ctor)]
    (when (empty? ctors)
      (throw (IllegalArgumentException.
              (str "No matching ctor found for " klass))))
    (let [^"[Ljava.lang.Object;" args (into-array Object args)]
      (.newInstance ^Constructor (first ctors) args))))