2012-02-06 20 views
10

, nasıl bir değişkene depolanan bir java sınıfı kullanılır?Clojure'da, bir java Sınıfını dinamik olarak nasıl kullanabilirim? Clojure içinde

Aşağıdaki kodu nasıl gidermeliyim?

(def a java.lang.String) 
(new a "1"); CompilerException java.lang.IllegalArgumentException: Unable to resolve classname: a 

Ve bu neden iyi çalışıyor?

(def a str) 
(a "1") 
+2

Bunun daha önce ortaya çıktığını ve gerçekten de bunun olduğunu düşündüm: [Clojure: String sınıfı adından yeni örnek oluşturma] (http: // stackoverflow.com/q/3748559/232707) 'clojure.lang.Reflector/invokeConstructor' ve başka bir yaklaşımdan bahseden Chouser tarafından verilen büyük bir yanıtla, "statik + hızlı" ve "dinamik + yavaş" arasında bir tür orta zemin İlginizi çekebilecek olan "çok dinamik + bir kez yavaş, statik + hızlı sonra"). –

cevap

8

en zarif çözüm new aynı yapar construct yazmaktır ama dinamik bir sınıf alabilir:

(defn construct [klass & args] 
    (clojure.lang.Reflector/invokeConstructor klass (into-array Object args))) 
(def a HashSet) 
(construct HashSet '(1 2 3)); It works!!! 

Bu çözüm @mikera 'ın cevabı (yorumlar) sınırlandırılmasını üstesinden gelir.

Özel invokeConstructor'un başka bir soruyu yanıtlama olasılığını bulan @Michał Marczyk'a teşekkürler: Clojure: how to create a record inside a function?.

Başka bir seçenek, çağrıyı yapıcıya anonim işlev olarak kaydetmektir. Bizim durumumuzda:

A:

(def a #(String. %1)) 
(a "111"); "111" 
6

sorun Clojure özel formlarda bir dizi kullanarak Java birlikte çalışma uygular şudur:

user=> (doc new) 
------------------------- 
new 
Special Form 
    Please see http://clojure.org/special_forms#new 
nil 

temelde "normal" Clojure sözdizimi Java çağrılırken handier yapılara izin vermek için değiştirilmiş demektir bu. Dinamik Java saf yansıma çözüm gerektirdiğinde, eval artırabilen:

user=> (def a String) ; java.lang package is implicitly imported 
#'user/a 
user=> `(new ~a "test") ; syntax quote to create the correct form 
(new java.lang.String "test") 
user=> (eval `(new ~a "test")) ; eval to execute 
"test" 

aynı strateji method invocation gibi tüm diğer birlikte çalışma özel formlar ile çalışır.


DÜZENLEME: Java yansıma API üzerinden daha performanslı alternatif @mikera den answer da bakmayı.

7

bu şekilde bir tanımladığınızda, sonra 2 seçeneğiniz var bir java.lang.Class

(def a java.lang.String) 

(type a) 
=> java.lang.Class 

içeren bir var olsun Construct yansıtma API'sini kullanarak Java yapıcısını bularak dinamik olarak yeni örneği. Yehonathan işaret gibi yapıcı imza tanımlanan kesin sınıfını kullanmak gerekir unutmayın (doğru imzayı bulamaz gibi bir alt sınıf işe yaramaz):

(defn construct [klass & args] 
    (.newInstance 
    (.getConstructor klass (into-array java.lang.Class (map type args))) 
    (object-array args))) 

(construct a "Foobar!") 
=> "Foobar!" 

B: kullanarak Clojure en Construct , bir değerlendirme gerektirir Java birlikte çalışma,: her eval deyimi için Clojure derleyici çağırmak yükünden kaçınmak çünkü yöntem A ölçüde daha hızlı olduğunu

(defn new-class [klass & args] 
    (eval `(new ~klass [email protected]))) 

(new-class a "Hello!") 
=> "Hello!" 

Not (yaklaşık 60x daha hızlı benim makinede), ben esas olarak düşünüyorum.

+0

Bağımsız değişken olarak, imzada tanımlanan sınıfın türetilmiş bir sınıfını geçerken 'yapı 'ile ilgili bir sorun var. Örneğin '(HashSet '(8)' i kurarken' (yeni HashSet '(8)) 'bir istisna yaratır. – viebel

+0

Seçenek B: iyi görünüyor. 'Eval' kullanımı nedeniyle herhangi bir sınırlama/endişe var mı? – viebel

+1

Potansiyel dezavantajları: Harici olarak sağlanan değerler için dikkat edin - bir kod enjeksiyon güvenlik riski olabilir. Ayrıca değerlendirmenin Clojure derleyicisini çağırdığı için ek bir ek yükü vardır. Dikkatli olmamanız durumunda sürmesi zor olan bazı "akıllı" kodlara da yol açabilir. Genel olarak değerlendirmeler dikkatle kullanıldığında iyidir. – mikera

İlgili konular