下に示すクラス point
は、ひとつのインスタンス変数 x
と二つのメソッド get_x
、 move
を定義しています。インスタンス変数 x
の初期値は 0
です。 x
は mutable
と宣言されているので move
メソッドは x
の値を変更できます。
#
class point = object val mutable x = 0 method get_x = x method move d = x <- x + d end;;
class point : object val mutable x : int method get_x : int method move : int -> unit end
point
クラスのインスタンスである「点」 p
を生成してみましょう。
#
let p = new point;;
val p : point = <obj>
p
の型は point
であることに注意して下さい。これは上のクラス定義の際に自動的に定義された略記です。型 point
はオブジェクト型 <get_x : int; move : int -> unit>
を意味します。オブジェクトの型は、そのオブジェクトが属するクラスが持つメソッドとその型を並べたものになります。
p
のメソッドを呼び出してみます。
#
p#get_x;;
- : int = 0
#
p#move 3;;
- : unit = ()
#
p#get_x;;
- : int = 3
クラス定義本体の評価は、オブジェクトが生成される時にのみ行われます。そのため以下の例では、インスタンス変数 x
はふたつ異なるオブジェクトで異なる値に初期化されます。
#
let x0 = ref 0;;
val x0 : int ref = {contents = 0}
#
class point = object val mutable x = incr x0; !x0 method get_x = x method move d = x <- x + d end;;
class point : object val mutable x : int method get_x : int method move : int -> unit end
#
new point#get_x;;
- : int = 1
#
new point#get_x;;
- : int = 2
クラス point
は x
の初期値を引数として取るよう定義することもできます。
#
class point = fun x_init -> object val mutable x = x_init method get_x = x method move d = x <- x + d end;;
class point : int -> object val mutable x : int method get_x : int method move : int -> unit end
関数定義と同じように、次のように書くこともできます。
#
class point x_init = object val mutable x = x_init method get_x = x method move d = x <- x + d end;;
class point : int -> object val mutable x : int method get_x : int method move : int -> unit end
こうすると、クラス point
のインスタンスは x
の初期値を引数として取り、あたらしい「点」オブジェクトを返す関数になります。
#
new point;;
- : int -> point = <fun>
#
let p = new point 7;;
val p : point = <obj>
引数 x_init
はクラス定義の全体で参照することができます。
例えば、次の get_offset
メソッドは「点」オブジェクトの初期位置からの相対的な位置を返します。
#
class point x_init = object val mutable x = x_init method get_x = x method get_offset = x - x_init method move d = x <- x + d end;;
class point : int -> object val mutable x : int method get_offset : int method get_x : int method move : int -> unit end
クラスのオブジェクト定義本体の前に、式を評価して変数に束縛することもできます。これはオブジェクトがある一定の性質を満たすようにするのに便利です。次の例では、「点」を一番近くの格子点にあわせるようにしています。
#
class adjusted_point x_init = let origin = (x_init / 10) * 10 in object val mutable x = origin method get_x = x method get_offset = x - origin method move d = x <- x + d end;;
Class adjusted_point : int -> object val mutable x : int method get_offset : int method get_x : int method move : int -> unit end
(また、x_init
座標が格子点上にない時に、例外を発生するようにもできるでしょう。) ここでは point
クラス定義を origin
の値とともに呼び出すことによっても同じことが実現できます。
#
class adjusted_point x_init = point ((x_init / 10) * 10);;
class adjusted_point : int -> point
別の方法として、特別な生成関数として定義をすることもできます。
#
let new_adjusted_point x_init = new point ((x_init / 10) * 10);;
val new_adjusted_point : int -> point = <fun>
しかし、一般的には前に述べたやり方の方がよいのです。なぜならば、格子点上にくるよう調節する機能がクラス定義の一部になっていて、継承することができるからです。
これは、他のプログラミング言語でいうところのクラス構築子に相当します。このようにして、同じクラスに属するオブジェクトを異なったやり方で初期化する構築子を定義することができます。同様の機能を実現する別の方法として、 3.4 節「初期化」で述べる初期化子を用いる方法があります。