インスタンスの生成(Instance creation)

インスタンスの生成と初期化は総称関数initializemakeにより制御される。

概観(Overview)

インスタンスの生成と初期化は次のように行われる。

  1. ユーザがmakeを呼び、クラスとキーワード引数の集合を指定する。

  2. システム提供のmakeメソッドをシングルトンスペシャライザにより 特殊化されたユーザ定義のメソッドにより隠す(shadow)という選択もあり得る。 これによりユーザメソッドがmakeのすべての引数を引き取り、それらに基づき、 おそらくは組み合わせて、実際の初期化を提供することができる。典型的には、 メソッドはnext-methodと、新しい、もしくは修正されたキーワード引数を用いて、 インスタンスの生成、初期化を実際に行う。

  3. デフォルトの makeメソッドはそのキーワード引数、すなわち 与えられた初期化引数(supplied initialization arguments)を調べる。 次に、makeデフォルトの初期化引数(defaulted initialization arguments) 集合を作成する。それらは、与えられた初期化引数に、そのクラスまたは上位クラスで デフォルト値が定義されている引数を加えることにより、インスタンスの生成、初期化に 用いられる。

  4. デフォルトのmakeメソッドはインスタンスに割り付けを行い、値を与えられる スロットの初期化を行う。キーワード初期化可能なものはデフォルト初期化引数の対応する 値により初期化される。キーワード初期化可能ではないがデフォルト初期値指定を持つ スロットは適切な値を割り当てられる。

  5. 次に初期化されたインスタンスとデフォルト初期化引数に対してinitializeを呼ぶ。

  6. それぞれのinitializeメソッドは典型的にはnext-method()を呼び、 (必要なinitargsにアクセスするには#keyを使って)必要な初期化を実行する。 (ステップ4で処理されたスロットは初期化する必要はないことに注意。)

  7. デフォルトのmakeメソッドはinitializeへの呼び出しの返り値は無視して、 インスタンスを返す。

makeとinitializeの定義(Definitions of make and initialize)

make class #key  #all-keys  => instance [Generic Function]
makeはキーワードと値の組で指定された特性を持つ、classのインスタンスを返す。

Dylanの仕様はmakeの返す値が新しく割り付けられたインスタンスであるか、 既に作成済みのインスタンスを返してもよいのかどうかについては規定しない。 新しいインスタンスに割り付けを行った場合には、makeはそのインスタンスを返す前に initializeを呼ぶことになる。

返されるオブジェクトはclassの一般インスタンスであることが保証されるが、 必ずしも直接インスタンスであるとは限らない。この自由度のおかげでmakeを 抽象クラスに対して呼ぶことができるようになる。すなわち、抽象クラスの具象下位クラスの どれかの直接インスタンスをインスタンシエイトして返すことができる。

(make上のデフォルトメソッドはclassの新しく割り付けられた 直接インスタンスを返すことに注意。)

プログラマは特定のクラスに対し、singletonスペシャライザにより特殊化された メソッドを定義することでmakeをカスタマイズできる。これらのメソッドでは、 必要なら、next-methodを呼んでデフォルトのmakeの動作を利用できる。

カスタマイズ化がなされなければ、デフォルトのmakeメソッドが呼ばれる。

make class #rest supplied-initialization-arguments #key
            => instance    [Method]
makeのデフォルトメソッドは次を行なう。

classが抽象クラスの時はエラーを発信する。インスタンス化可能な抽象クラスでは makeに対するそれ自身のメソッドで このメソッドを上書きしなければならない。

classまたはその上位クラスに対する延期された(deferred)評価でまだ実行されていない ものがあれば、それがまず第一に実行される。

もしsupplied-initialization-argumentsが重複するキーワードを持つなら、 makeは最左の出現を用いる。(これは関数呼び出しにおける#key引数の規約と 整合的である。)

makesupplied-initialization-argumentsから始め、デフォルト初期化引数の 構成を行う。これは、classまたはその上位クラスでデフォルト値が定義されている 初期化引数を付加することにより、インスタンスの生成と初期化に用いられる。 必須初期化引数がデフォルト初期化引数中に存在しなかった場合、デフォルト初期化引数の どれかがそのクラスを初期化するのに有効でない(すなわち、スロット初期化にも使えないし、 classのインスタンスに適用可能なinitializeメソッドのどれにも認識されないような キーワードがある)場合にはmakeはエラーを発信する。 デフォルト初期化引数を作成するのに用いられる初期化関数は、初期化引数が提供された 初期化引数に実際に見あたらないときに限り呼ばれることに注意せよ。

次にmakeはインスタンスに割り付けを行い、値を計算できるスロットすべてを初期化する。 スロットがキーワード初期化可能で対応する初期化引数がデフォルト初期化引数内に出現する なら、そのスロットはその値に設定される。それ以外でスロットがデフォルト初期値指定を持つなら、 それ(初期値または初期化関数を呼んだ結果)が初期値を与えるのに用いられる。 どちらの場合でも、値がスロットで宣言されている型でないならエラーとなる。

上記のインスタンス割り付け及び初期化関数の呼び出しの正確な順序は定められていない。

makeは初期化されるインスタンスとデフォルト初期化引数に対してinitializeを 呼び、任意の、指示された(custom)、もしくは複雑な初期化でも行う。 ユーザ定義のinitializeメソッドは、特定の初期化引数にアクセスするためには #keyパラメータを、初期化引数すべてにアクセスするためには#restを 使うことができる。もしも、すべての初期化引数が調べられて、supplied initialization argumentsが重複したキーワードを持っていたなら、そのキーワードに対する最左の 項目以外が出現する(present)かどうかは定められていない。

makeinitializeへの呼び出しの返却値はすべて無視して、インスタンスを返す。

initialize instance #key  #all-keys  [Generic Function]
initialize総称関数は初期値や初期化関数では簡単には表せないインスタンスの初期化を ユーザが処理する方法を提供する。これは典型的には初期化引数やスロット値の入力を使った計算が 必要である、または一つの計算を複数のスロットを初期化するのに使う必要があるといった場合 である。

例えば、次の例では<triangle>クラスは3辺を持つインターフェイスを表して いるが、実際には2辺と1つの角度を格納している。

define class <triangle> (<shape>)
  slot side-a, required-init-keyword: side-a:;
  slot side-b, required-init-keyword: side-b:;
  slot angle-C;
  virtual slot side-c, required-init-keyword: side-c:
end class <triangle>;

define method initialize (x :: <triangle>,
                           #key side-a, side-b, side-c)
  next-method();
  x.angle-C := three-sides-to-angle (side-a, side-b, side-c);
end method initialize;

より特定されていないクラスの初期化が最初に確実に実行されるようにするため、すべての initializeメソッドはnext-methodを非常に早く(very early)呼ぶように するのが慣習(convention)になっている。 Dylan処理系はnext-methodを呼ばないinitializeのメソッドに対しては コーディングスタイルに関するウォーニングを出してもよい。

総称関数initializeはすべてのキーワードを許容し(permits)、必須はない。 このようにするのはキーワード引数のチェックはmakeのデフォルトメソッドですでに 実行されているからである。

initialize instance :: <object> #key [Method]

デフォルトのinitializeメソッドは何もしない。モジュール化して書かれた initializeメソッドではnext-method()が自由に使えるようにするためである。

初期化引数の継承(Initialization Argument Inheritance)

スロットがキーワード初期化可能なら、スロットについては何もいわずに、 初期化引数のデフォルト値だけを変えることができる。

次の例のfavorite-beverageのデフォルトの変更にこれが示されている。

define class <person> (...)
  slot favorite-beverage, init-value: #"milk",
                    init-keyword: favorite-beverage:;
  slot name required-init-keyword: name:;
end class <person>;

define class <astronaut> (<person>)
  keyword favorite-beverage: init-value: #"tang";
  keyword name: init-value: "Bud";
end class <astronaut>;

もう一つの一般的な使い方は、上記のname:キーワードで示されているように、上位クラスで 必須とされているデフォルトを与えることである。これにより、<astronaut>に対して makeを呼ぶ場合、name:キーワードはもう必須とはされない。

初期化引数指定の継承は次のように定義される。4つの場合がある。

  1. required-init-keyword:を用いた初期化キーワードKを持つスロット指定は、 初期化引数指定required-init-keyword: Kがクラス定義で指定されていた場合と 全く同様に扱われる。

    初期化キーワードとさらに、初期値または初期化関数、を両方持つスロット指定は、 初期化引数指定で、初期化キーワードと初期値または初期化関数を含むものと同値ではない。 前者はスロットのデフォルト値を直接定めているだけで、デフォルト初期化引数には影響しない。 これに対し、後者ではデフォルト初期化引数に影響があり、間接的にスロットのデフォルト値も 変わることになる。

  2. 初期化引数が初めて指定された場合、この時にはどの上位クラスからも継承を行わない。

    a.type:引数、そのデフォルトは<object>であるが、この引数は 初期化引数に要求される型を指定する。

    b. 初期化引数がrequired-init-keyword:で指定されていたなら、その引数は必須である。 そうでなければ、付加的(optional)である。

    c. 初期化引数がinit-keyword:付きで与えられた場合、それは(デフォルト初期化引数の 一つである)その初期化引数指定のデフォルト値を与えるためにデフォルトのmake メソッドにより使われる初期値指定を与えることができる。これはinit-value:で指定される値、 またはinit-function:で指定される(値が必要な時に毎回呼び出される)関数である。

  3. 単一の上位クラスから継承された初期化引数について初期化引数指定が定められている場合。

    a.その型は継承された初期化引数の型の部分型でなければならない。

    b.上書きする初期化引数指定がrequired-init-keyword:を用いている場合、 または継承された初期化引数指定が必須で上書きする初期化引数指定がinit-value:init-function:も使っていない場合、その初期化引数は必須である。 上書きする初期化引数指定がrequired-init-keyword:を使っている時、 継承された初期化引数でのinit-valueまたはinit-function はすべて棄てられる。

    c.それ以外の場合、初期化引数は付加的である。上書きする指定がinit-value: またはinit-function:を持つなら、それはクラスをインスタンシエイトするときに デフォルト初期化引数を計算するのに用いられる。そうでない場合、継承された初期値指定が 用いられる。

  4. 初期化引数指定が複数の上位クラスから継承されている場合。上位クラス達が初期化引数に 対して全く同じ定義を持っているなら、その定義を単に継承すればよい。定義が異なるなら、 これらの他の(上位)クラスを組み合わせることになる、そのクラスは、上で説明したように 継承される引数指定すべてと整合的な引数指定を提供しなければならない。

3.b. は上位クラスで用いられている初期化引数が必須になるように 下位クラスで強制できるが、使われるべきデフォルト値を指定しないで 必須初期化引数を付加的にするよう強制することはできない ということを意味している。

クラスに割り付けられるスロットの初期化(Initialization of Class Allocated Slots)

割り付けclassおよびeach-subclassでの初期化は次のように行われる。スロットが キーワード初期化可能かどうか、デフォルト値の計算方法が何か与えられているかに応じて、 4通り考えるべき場合がある。

1.スロットがキーワード初期化可能で、そのスロットのデフォルト値がない(すなわち、 init-value:init-function:も指定されていない)場合。スロット値は変化せず、 現在初期化されていないなら、そのままとなる。

2.スロットはキーワード初期化可能ではないが、(init-value:またはinit-function:で 指定された)デフォルト値がある場合。初期値(または初期化関数呼び出しの値)はこのスロットを 共有しているクラス上のmake の最初の呼び出しの前、またはその最中、のどこかの時点で、 そのスロットを初期化するのに用いられる。

3.スロットはキーワード初期化可能だが、対応する初期化引数がmakeの呼び出しのデフォルト 初期化引数にはない場合。スロット値は変化せず、現在初期化されていないなら、そのままとなる。

4.スロットがキーワード初期化可能で、対応する初期化引数がmakeの呼び出しのデフォルト 初期化引数にある場合。スロットは無条件で初期化引数の値に設定される。

スロットの初期化の検査(Testing the Initialization of a Slot)

プログラムはスロットが初期化されているかどうか、検査することができる。

slot-initialized?   instance getter   =>  boolean [Function]
slot-initialized?は、getter総称関数によりアクセスされるinstanceのスロットが 初期化されているなら、真を返す。そのスロットが初期化されていないなら、偽が返る。

slot-initialized?は、getterinstanceのスロットにアクセスしないなら、 エラーを発信する。

スロットを初期化されていない状態に戻す機構はない。


目次 索引 クラス(章見出し)、 型に対する反射的操作(次節)