Compiler
という名のクラスをJavaからインポートしたいとします。
(ns leiningen.zinc
"Typesafe scala zinc incremental compiler plugin for leiningen."
(:require [leiningen.classpath :as classpath]
[leiningen.core.main :as main])
(:import (com.typesafe.zinc Compiler Setup)))
一見問題なさそうですが、ns
マクロを評価すると、下記のようなエラーが出力されます。
Caused by: java.lang.IllegalStateException: Compiler already refers to: class clojure.lang.Compiler in namespace: leiningen.zinc
com.type.safe.zinc.Compiler
が、暗示的に参照されているclojure.lang.Compiler
と衝突しているようです。
ns
マクロは、デフォルトで、clojure.core
内の全ての関数、java.lang
内の全てのクラス、そしてclojure.lang.Compiler
をマップに加えてから生成されます。
まず思いつくのは、require
の:asオプションで別名をつけてやることで名前衝突を回避できるかどうかですが、この場合、clojure.lang.Compiler
は既にロードされているので、ライブラリをロードするrequire
を用いることができませんし、as
は名前空間に別名をつけるもので、クラスは対象でありません。
では、import
側でエイリアスをつけることは可能でしょうか?docstringとソースコードを見る限り、サポートされていないようです。
更に調べると、referを用いると、名前空間内のパブリック変数を参照し、かつ:exclude
、:only
、:rename
のフィルターをかけることができるようです。
まずは、名前空間のマップにバインドされている内容の一覧を取得してみましょう。
leiningen.zinc=> (ns-map 'leiningen.zinc)
{primitives-classnames #'clojure.core/primitives-classnames, +' #'clojure.core/+', Enum java.lang.Enum,
...
number? #'clojure.core/number?, Compiler clojure.lang.Compiler,...
名前空間のマップはVarと、Classの両方を含んでいます。refer
はVarにしか適用できませんので、refer
のフィルタをクラスであるclojure.lang.Compiler
に適用させることはできません。
そこで、ns-unmap
を用いて、名前空間マップからCompilerのエントリを取り除き、それから改めて、import
をns
マクロのキーワードとしてではなく、import
マクロを直接呼び出すことで、Compiler
エントリを追加しています。
(ns leiningen.zinc
"Typesafe scala zinc incremental compiler plugin for leiningen."
(:require [leiningen.classpath :as classpath]
[leiningen.core.main :as main]))
(ns-unmap *ns* 'Compiler)
(import (com.typesafe.zinc Compiler Setup))
...
このような操作が煩雑であれば、import
をせず、com.typesafe.zinc.Compiler
をフルパスで指定して使えば良いでしょう。