Có hai cách hay để thực hiện việc này. Điều nào là tốt nhất phụ thuộc vào hoàn cảnh cụ thể.
Đầu tiên là phản ánh:
(clojure.lang.Reflector/invokeConstructor
(resolve (symbol "Integer"))
(to-array ["16"]))
Đó là như gọi (new Integer "16")
... bao gồm bất kỳ đối số ctor khác mà bạn cần trong vector to-mảng. Điều này là dễ dàng, nhưng chậm hơn trong thời gian chạy hơn sử dụng new
với các gợi ý loại đầy đủ.
Lựa chọn thứ hai là càng nhanh càng tốt, nhưng một chút phức tạp hơn, và sử dụng eval
:
(defn make-factory [classname & types]
(let [args (map #(with-meta (symbol (str "x" %2)) {:tag %1}) types (range))]
(eval `(fn [[email protected]] (new ~(symbol classname) [email protected])))))
(def int-factory (make-factory "Integer" 'String))
(int-factory "42")
Điểm mấu chốt là mã eval định nghĩa một chức năng mang tính chất như make-factory
làm. Đây là chậm - chậm hơn ví dụ phản chiếu ở trên, do đó, chỉ thực hiện càng ít càng tốt, chẳng hạn như một lần cho mỗi lớp học. Nhưng bạn đã thực hiện rằng bạn có một hàm Clojure thông thường mà bạn có thể lưu trữ ở đâu đó, trong một var như int-factory
trong ví dụ này hoặc trong bản đồ băm hoặc véc-tơ tùy thuộc vào cách bạn sẽ sử dụng nó. Bất kể, chức năng nhà máy này sẽ chạy ở tốc độ được biên dịch đầy đủ, có thể được HotSpot inline, vv và sẽ luôn chạy nhiều hơn nhanh hơn so với ví dụ phản chiếu.
Khi bạn giao dịch cụ thể với các lớp được tạo bởi deftype
hoặc defrecord
, bạn có thể bỏ qua danh sách loại vì các lớp đó luôn có chính xác hai ctors với các vị trí khác nhau. Điều này cho phép một cái gì đó như:
(defn record-factory [recordname]
(let [recordclass ^Class (resolve (symbol recordname))
max-arg-count (apply max (map #(count (.getParameterTypes %))
(.getConstructors recordclass)))
args (map #(symbol (str "x" %)) (range (- max-arg-count 2)))]
(eval `(fn [[email protected]] (new ~(symbol recordname) [email protected])))))
(defrecord ExampleRecord [a b c])
(def example-record-factory (record-factory "ExampleRecord"))
(example-record-factory "F." "Scott" 'Fitzgerald)
Tuyệt vời! Lựa chọn thứ hai rõ ràng là một kỹ thuật rất chung chung. Tôi đã sử dụng nó theo một cách khác. – chris