2012-02-06 5 views
10

Clojureでは、どのように変数に格納されているJavaクラスを使用するには?Clojureでは、どのようにJavaクラスを動的に使用するのですか?

次のコードはどのように修正する必要がありますか?

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

なぜこれがうまく機能しますか?

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

私はこれが以前に起きていたと確信していました:[Clojure:String class nameから新しいインスタンスを作成する]を参照してください(http:// stackoverflow。Chouserの偉大な答えで、 'clojure.lang.Reflector/invokeConstructor'と別のアプローチ、「静的+高速」と「動的+遅い」の中間的な中間地点を挙げています(com/q/3748559/232707)それを「とてもダイナミック+スロー、静的+速い」と呼びます)、これはあなたの興味を引くかもしれません。 –

答えて

8

最もエレガントな解決策はnewと同じように行いますが、動的にクラスを受信することが可能であるconstructを書くことです:

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

このソリューションは@mikeraの答えの制限を克服する(コメントを参照してください)。

@Michał Marczykのおかげで、invokeConstructorの別の質問にお答えします:Clojure: how to create a record inside a function?

もう1つのオプションは、コンストラクタへの呼び出しを匿名関数として保存することです。私たちの場合:

A:

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

問題はClojureのは、特別なフォームの数を使用してJava相互運用機能を実装していることである:

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

これは基本的に「通常の」Clojureの構文はJavaを呼び出すときに手軽構造を可能にするように変更されることを意味します。あなたの動的なJavaへのナイーブ反射ソリューションが必要として、あなたはevalを活用することができます

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" 

同じ戦略がmethod invocationのように、他のすべての相互運用の特別な形式で動作します。


EDIT:JavaリフレクションAPIを介して、より性能の代替用@mikeraからanswerでも見てください。

7

は、あなたがこのように定義するとき、あなたはその後、2つのオプションを持っているのjava.lang.Class

(def a java.lang.String) 

(type a) 
=> java.lang.Class 

を含むVARを得る構築しますリフレクションAPIを使用してJavaコンストラクタを見つけることによって、新しいインスタンスを動的に更新します。

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

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

B:Yehonathanが指摘するようにあなたは、コンストラクタのシグネチャで定義された正確なクラスを(それが正しい署名を見つけることができませんとして、サブクラスが動作しません)を使用する必要があることに注意してくださいClojureののを使用して構築evalのを必要とするJava相互運用機能、:方法Aはかなり速いこと

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

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

注(約60倍速く私のマシン上で)、私はそれが各evalの文のClojureのコンパイラを呼び出すのオーバーヘッドを回避主な理由だと思います。

+0

署名に定義されたクラスの派生クラスを引数として渡すときに 'construct'に問題があります。例えば ​​'(HashSet '(8)を構築する)'( 'new HashSet'(8))'は例外を引き起こします。 – viebel

+0

オプションB:よく見えます。 'eval'の使用に起因する制限/懸念はありますか? – viebel

+1

evalの潜在的な弱点:外部から提供される値に注意してください。コードインジェクションのセキュリティリスクが存在する可能性があります。また、evalにはClojureコンパイラを呼び出すためのオーバーヘッドがあります。それはまた、あなたが慎重でない場合、維持するのが難しい "巧妙な"コードにつながる可能性があります。一般的にevalは慎重に使用すると問題ありません。 – mikera

関連する問題