続いて、今度はClojureでコンストラクタを持つクラスを作成してみます。
foo.clj
(ns foo (:gen-class :init init :state state :constructors {[String] []} :methods [[decorate [char] String]])) (defn -init [val] [[] val]) (defn -decorate [this c] (let [decoration (reduce #(str %1 "" %2) (repeat 3 c))] (str decoration (.state this) decoration)))
少し解説。
initキーワードで、初期化関数を指定します。
:init init
定義する関数は、今回はこのようにしました。
(defn -init [val] [[] val])
関数の引数には、コンストラクタの引数が渡ってきます。
ここで、コンストラクタの定義は以下のようにしているので
:constructors {[String] []}
引数には、String型の値が渡ってきます。
doc-stringより、initの方は
If supplied, names a function that will be called with the arguments
to the constructor. Must return [ [superclass-constructor-args] state]
If not supplied, the constructor args are passed directly to
the superclass constructor and the state will be nil
と書かれているので、initキーワードで指定した関数の戻り値は親クラスのコンストラクタ引数、そしてstateのベクタだと言っています。よって、-init関数の戻り値は
[[] val]
なので、親クラスのコンストラクタには引数無し、そしてstateには引数の値をそのまま設定しています。
なお、constructorsキーワードの方ですが、
:constructors {[param-types] [super-param-types], ...}
という定義のため、
:constructors {[String] []}
という定義は、このクラスのコンストラクタはString型の引数を持ち、親クラスのコンストラクタには引数がない、ということを表しています。
なお、状態を表すstateキーワードには
:state state
そのまま、stateという名前で参照できるようにしました。
public class Caller { public static void main(String[] args) { foo f = new foo("Hello Clojure"); System.out.println(f.state); // => Hello Clojure System.out.println(f.decorate('*')); // => ***Hello Clojure*** } }
コンストラクタに、Stringの引数を与えられるようになっています。また、stateも普通に参照可能です。
続いて、Clojureから呼び出してみます。
(def f (foo. "Hello World")) (println (.state f)) ;; => Hello World (try (set! (.state f) "Hoge") (catch Exception e (println (.toString e)))) ;; => java.lang.IllegalAccessException: Can not set final java.lang.Object field foo.state to java.lang.String
Javaの時と同じように使用可能です。
stateですが、注意点としてはコンストラクタで初期化する以外に方法がなく、かつ1度指定した参照は変更することができません。stateはfinal修飾された形でJavaのフィールドにコンパイルされるからです。
こんな感じに。
$ javap classes/foo Warning: Binary file classes/foo contains foo public class foo { public final java.lang.Object state; public static {}; public foo(java.lang.String); public java.lang.Object clone(); public int hashCode(); public java.lang.String toString(); public boolean equals(java.lang.Object); public java.lang.String decorate(char); public static void main(java.lang.String[]); }