3.1 クラスとオブジェクト

下に示すクラス point は、ひとつのインスタンス変数 x と二つのメソッド get_xmove を定義しています。インスタンス変数 x の初期値は 0 です。 xmutable と宣言されているので 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
    

クラス pointx の初期値を引数として取るよう定義することもできます。

#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 節「初期化」で述べる初期化子を用いる方法があります。