7章Objectsのクラス関連の和訳
【追記】7章Objectsの全てを日本語訳しました。
https://nptcl.github.io/npt-japanese/docs/ansicl/7.html
https://nptcl.github.io/npt-japanese/md/ansicl/7.html
ふたつのリンクは、レイアウトが違うだけでどちらも同じ内容なので好きな方を見てください。
リンク先はDictionaryも翻訳してます。
ただし翻訳してあるのは7章だけです。
ANSI Common Lispの規格書の、「7. Objects」の一部の和訳です。
必要になったので必死に翻訳しました。
訳があっているかどうかは知らん。
翻訳元は下記の通り。
draft proposed American National Standard for Information Systems Programming Language Common Lisp Version 15.17R, X3J13/94-101R. Fri 12-Aug-1994 6:35pm EDT http://www.cs.cmu.edu/afs/cs/Web/Groups/AI/lang/lisp/doc/standard/ansi/dpans/
HyperSpecだと下記の通り。
7. Objects http://www.lispworks.com/documentation/HyperSpec/Body/07_.htm
7.6 Generic Functions and Methods
以降は長いのでまた今度。
【追記】続きを投稿しました。7章Objectsのジェネリック関数関連の和訳
目次
7. オブジェクト 7.1 オブジェクトの作成と初期化 7.1.1 初期化引数 7.1.2 初期化引数の有効性の宣言 7.1.3 初期化引数のデフォルト値 7.1.4 初期化引数の規則 7.1.5 Shared-Initialize 7.1.6 Initialize-Instance 7.1.7 Make-InstanceとInitialize-Instanceの宣言 7.2 インスタンスのクラスの変更 7.2.1 インスタンスの構造の修正 7.2.2 新しく追加された局所スロットの初期化 7.2.3 インスタンスのクラスの更新のカスタマイズ 7.3 インスタンスの再初期化 7.3.1 再初期化のカスタマイズ 7.4 メタオブジェクト 7.4.1 標準メタオブジェクト 7.5 スロット 7.5.1 スロットの紹介 7.5.2 スロットへのアクセス 7.5.3 スロットの継承とスロットオプション 【別投稿】7.6 ジェネリック関数とメソッド
7. オブジェクト
7.1 オブジェクトの作成と初期化
ジェネリック関数make-instance
は、クラスの新しいインスタンスを作成し返却します。
最初の引数はクラスか、クラスの名前であり、残りの引数は初期化引数リストです。
新しいインスタンスの初期化は、いくつかのステップから成ります。
内容は次のようになります。
指定されなかった初期化引数の値に対して、明に指定された初期化引数とデフォルト値を結びつけるステップ。
初期化引数の有効性をチェックするステップ。
インスタンスの記憶領域を確保するステップ。
スロットに値を埋めるステップ。
そして追加の初期化を行うためにユーザーが提供したメソッドを実行するステップ。
make-instance
の各ステップはジェネリック関数により実装されているため、
それぞれのステップをカスタマイズする仕組みが提供されています。
加えて、make-instance
自体がジェネリック関数であるため、自身もカスタマイズできます。
オブジェクトシステムは各ステップに対して、システムで提供されたメソッドを用意しています。 メソッドは初期化全体の手順の標準的な振る舞いを定義したものです。 標準の振る舞いは、下記の4つの簡単な仕組みによって、初期化を制御することができます。
スロットの初期化引数としてのシンボルの宣言。 初期化引数は
defclass
のスロットオプションである、:initarg
を使うことで宣言できます。 これは、make-instance
の呼び出し時に、 スロットの値を設定するための仕組みとして提供されたものです。初期化引数のデフォルト値フォームの指定。初期化引数のデフォルト値フォームは、
defclass
のクラスオプションである:default-initargs
を使うことで定義できます。 もし初期化引数がmake-instance
の引数として明に提供されなかった場合、 デフォルト値フォームはdefclass
が宣言されたレキシカル環境の中で評価されます。 そして評価された結果の値は、初期化引数の値として使用されます。スロットのデフォルト初期化値フォームの提供。スロットのデフォルト初期化値フォームは、
defclass
のスロットオプション:initform
を使うことで宣言されます。 もしmake-instance
の引数かあるいは:default-initargs
のデフォルト値にて、 スロットに対応する初期化引数が与えられていなかった場合、 デフォルト値フォームはdefclass
が宣言されたレキシカル環境の中で評価されます。 そして評価された結果の値はスロットに格納されます。 局所スロットの:initform
フォームは、インスタンスが作成されたとき、 クラスの再定義によりインスタンスを更新するとき、 そしてインスタンスを違うクラスの定義に更新するときに使用されるでしょう。 共有スロットの:initform
フォームに関しては、 定義のときか、再定義のときに使用されます。initialize-instance
とshared-initialize
のメソッド定義。 スロットの値を設定するこれらの振る舞いは、 システムが提供するメソッドで提供されており、initialize-instance
は、shared-initilize
を呼び出すように実装されています。 ジェネリック関数shared-initialize
は初期化の部分を実装しており、 次の4つの状況で共有されています。 それは、インスタンス作成時、インスタンスの再初期化時、 クラスの再定義によるインスタンスの更新時、 そして違うクラス定義へインスタンスを更新するときです。 システムが提供するshared-initialize
のメソッドは、 スロットの値を更新するための上記の振る舞いを直接実装しているため、initialize-instance
は単純にshared-initialize
を呼び出すだけとなります。
7.1.1 初期化引数
初期化引数は、オブジェクトの作成と初期化を制御します。
よくキーワードを初期化引数の名前にするのが便利で使われますが、
初期化引数の名前はnil
を含むどんなシンボルでも使用できます。
初期化引数は、次の2つの方法である、
スロットの値を埋めるためか、
あるいは初期化メソッドの引数に提供するときに使用します。
単一の初期化引数は、両方の目的で使用されます。
初期化引数リストは、初期化引数の名前と値のプロパティリストです。
この構造は、通常のプロパティリストとして同一であり、
引数リストの&key
パラメーターとして処理される部分としても同一です。
これらのリストは、もし初期化引数の名前が初期化引数リストに複数現れた場合は、
もっとも左側に現れた値が指定され、残りのものは無視されます。
make-instance
の引数(最初の引数よりあとのもの)の形は、初期化引数リストです。
初期化引数はスロットと結び付けることができます。
もし初期化引数が初期化引数リストの中で値を持っている場合、
その値は新しく作成されたオブジェクトのスロットに格納されます。
もし:initform
フォームがスロットと結び付けられていた場合でも、初期化引数の方が上書きをします。
1つの初期化引数は、複数のスロットを初期化することができます。
共有スロットを初期化する初期化引数は、以前の値を置き換えて、共有スロットに値を格納します。
初期化引数はメソッドに結び付けることができます。
オブジェクトが作成されて、特定の初期化引数が与えられた場合、
ジェネリック関数であるinitialize-instance
, shared-initialize
, そしてallocate-instance
は、
キーワード引数のペアとして、初期化引数の名前と値とともに呼び出されます。
もし初期化引数の値が初期化引数リストで提供されていなかった場合は、
メソッドのラムダリストがデフォルト値を提供します。
初期化引数は次の4つの状況によって使用されます。 インスタンスの作成時、インスタンスの再初期化時、 クラス再定義によるインスタンスの更新時、そして違うクラス定義へのインスタンスを更新するときです。
初期化引数は特定のクラスのインスタンスの作成と初期化時に制御で使用されるため、 初期化引数は、クラスの「初期化引数は〜」のように記述します。
7.1.2 初期化引数の有効性の宣言
初期化引数は、4つの状況にて有効性がチェックされます。
初期化引数はひとつの状況では有効かもしれませんが、他はそうではないかもしれません。
例えば、システムで提供されたmake-instance
のメソッドのstandard-class
クラスを対象とした場合を考えます。
もし初期化引数が与えられていたものの、有効性としての宣言がされていなかった場合、
メソッドは初期化引数の有効性チェックにおいてエラーのシグナルを発することになります。
初期化引数の有効性の宣言は、次の2つの意味があります。
スロットを設定するときの初期化引数は、
defclass
のスロットオプションである:initarg
によって有効であるとして宣言されます。 スロットオプションの:initarg
は、スーパークラスから継承されます。 よって、クラスのスロット設定時の有効な初期化引数の集合は、 クラスとスーパークラスによって有効であると宣言されたスロット設定時の初期化引数の和集合となります。 初期化引数による値の設定は、4つの状況すべてにおいて有効です。メソッドの引数として与えられた初期化引数は、 これらメソッドの宣言によって有効であると定義されます。 メソッドのラムダリストとして定義されたキーワードパラメーターのキーワード名は、 全てのクラスの適用可能なメソッドの初期化引数となります。 適用されるメソッドのラムダリストにある
&allow-other-keys
の存在は、 初期化引数の有効性のチェックを無効にします。 よってメソッドの継承は、メソッドに引数として渡される有効な初期化引数の集合を制御します。 メソッドの定義を持ったジェネリック関数は、下記に示すものとして、 有効な初期化引数の宣言を守ります。関数
allocation-instance
,initialize-instance
とshared-initialize
により、 クラスのインスタンスを作成するとき。 クラスのインスタンスを作成するとき、 これらのメソッドにより有効だと宣言された初期化引数は有効です。関数
reinitialize-instance
とshared-initialize
により、インスタンスの再初期化が行われるとき。 インスタンスの再初期化が行われるとき、 これらのメソッドにより有効だと宣言された初期化引数は有効です。関数
update-instance-for-redefined-class
とshared-initialize
により、 再定義されたクラスにインスタンスを更新するとき。 再定義されたクラスにインスタンスを更新するとき、 これらのメソッドにより有効だと宣言された初期化引数は有効です。関数
update-instance-for-different-class
とshared-initialize
により、 違うクラスの定義にインスタンスを更新するとき。 違うクラスの定義にインスタンスを更新するとき、 これらのメソッドにより有効だと宣言された初期化引数は有効です。
クラスの有効な初期化引数の集合は、スロットの値の設定か、
あるいは初期化引数の前宣言として与えられる:allow-other-keys
に従った
メソッドの引数かのどちらかの初期化引数の集合です。
:allow-other-keys
のデフォルト値はnil
です。
もし初期化引数:allow-other-keys
の値がtrue
であるならば、
初期化引数の有効性の確認は無効となります。
7.1.3 初期化引数のデフォルト値
クラスオプションである:default-initargs
を使うことで、
初期化引数のデフォルト値フォームを提供することができます。
もしいくつかのクラスによって初期化引数が有効であると宣言された場合は、
デフォルト値フォームは違うクラスによって設定されるかもしれません。
このような場合では、:default-initargs
は継承された初期化引数によって提供されたデフォルト値が使用されます。
オプション:default-initargs
は、初期化引数へのデフォルト値の提供のみに使用されます。
このオプションでは、シンボルを有効な初期化引数の名前として宣言しません。
さらに、オプション:default-initargs
は、
インスタンス作成時における初期化引数のデフォルト値の提供としてのみ使用されます。
クラスオプションの引数である:default-initargs
は、
初期化引数の名前とフォームが交互に現れるリストです。
各フォームは、初期化引数に対応するデフォルト値のフォームです。
初期化引数のデフォルト値のフォームは、
make-instance
の引数に初期化引数として現れていなかった場合、
かつ、もっと特定的なクラスによってデフォルト値が定義されていなかった場合のみに、
評価されて使用されます。
デフォルト値のフォームは、defclass
フォームのレキシカル環境で評価されたものが提供され、
評価された結果は初期化引数の値として使用されます。
make-instance
に指定された初期化引数は、
デフォルトの初期化引数と結び付けられ、
デフォルト初期化引数リストを生成します。
デフォルト初期化引数は、初期化引数の名前と値を交互にリストにしたものです。
このリストは、指定されていない初期化引数のデフォルト値を決定するものであり、
また明示的に初期化引数が指定されたものは、
デフォルト初期化引数リストのより早く表れたもののリストとします。
デフォルト初期化引数は、クラス優先リストの順番に従ったクラスのデフォルト値に順番付されます。
:default-initargs
と:initform
では、どちらもスロットの初期化に使用されますが、
両者の間には目的に違いがあります。
クラスオプションである:default-initargs
は、ユーザーに対して、
初期化引数がスロットを初期化されているかどうか、
あるいはメソッドに渡されるかどうかを知ることなしに、デフォルト値を与える仕組みを提供します。
もしmake-instance
を呼ぶ際に、初期化引数を明示的に与えなかった場合はデフォルト値が使用されますが、
デフォルト値は呼び出し時に指定されたものとして呼び出されます。
対称的に、スロットオプションである:initform
は、
ユーザーがスロットのデフォルト値フォームを与えるための仕組みとして提供されます。
:initform
フォームはスロットの初期化に使用されますが、
ただmake-instance
に与えられた初期化引数に対応するスロットとの結びつきがなかった場合、
あるいは:default-initargs
にデフォルト値の指定がなかった場合のみ、:initform
にて初期化が行われます。
初期化引数のデフォルト値フォームの評価順序と、
:initform
フォームの評価順序は定義されてはいません。
もし評価順序が重要である場合は、
代わりにinitialize-instance
かshared-initialize
メソッドを使用してください。
7.1.4 初期化引数の規則
スロットオプションの:initarg
は、スロット対して複数定義されるかもしれません。
もし初期化引数に複数の定義がされるかもしれないときには、下記に示すルールが適応されます。
もし同じ初期化引数の名前が
:initarg
スロットオプションに複数現れた場合は、 初期化引数は複数のスロットを初期化できます。初期化引数の名前は、複数の初期化メソッドのラムダリストに現れます。
初期化引数の名前は、スロットオプションの
:initarg
と、初期化メソッドのラムダリストの両方に現れます。
もしmake-instance
に与えられた引数が、同じスロットを初期化するような複数の初期化引数であった場合、
さらに初期化引数が違った名前であったときは、初期化引数リストの最も左の初期化引数の値が採用されます。
もし複数の違った初期化引数が同じスロットを初期化する場合、
さらにスロットはデフォルト値を持っており、
make-instance
の引数には明示的に指定されていなかったときは、
初期化引数は最も特定的なクラスのクラスオプション:default-initargs
に現れる値が採用されます。
もしひとつの:default-initargs
クラスオプションが、
複数の初期化引数により同じスロットを初期化する場合、
さらにmake-instance
の引数には明示的に指定がなかったときは、
クラスオプション:default-initargs
の最も左側の値が採用され、
残りのデフォルト値フォームの値は無視されます。
make-instance
の引数として明示的に与えられた初期化引数は、
デフォルト初期化引数の左側に現れます。
例えば、クラスC1
とC2
が違うスロットに対してデフォルト初期化引数の値を与えた場合を考えます。
C1
はC2
よりも特定的であるとします。
C1
によって提供されたデフォルト初期化引数は、
デフォルト初期化引数リストにおいては、
C2
によって提供されたのデフォルト初期化引数の左側に位置します。
もし単一のクラスオプション:default-initargs
が、
2つの違ったスロットに対して初期化引数の値が与えられた場合、
クラスオプション:default-initargs
の最も左に位置する初期化引数が、
デフォルト初期化引数リストの最も左側に現れます。
もしスロットが:initform
フォームと:initarg
スロットオプションの両方を持っており、
さらに初期化引数が:default-initargs
によるデフォルト値により与えられているか、
あるいはmake-instance
の引数により与えられていた場合、
:initform
フォームは使われませんし評価もされません。
上記の規則の例を示します。
(defclass q () ((x :initarg a))) (defclass r (q) ((x :initarg b)) (:default-initargs a 1 b 2))
フォーム デフォルト初期化引数リスト スロットXの値 ---------- (make-instance 'r) (a 1 b 2) 1 (make-instance 'r 'a 3) (a 3 b 2) 3 (make-instance 'r 'b 4) (b 4 a 1) 4 (make-instance 'r 'a 1 'a 2) (a 1 a 2 b 2) 1
7.1.5 Shared-Initialize
ジェネリック関数shared-initialize
は、
インスタンスの作成時、インスタンスの再初期化時、
クラス再定義によるインスタンス更新時、違うクラスへのインスタンス更新時において、
インスタンスのスロット値を、初期化引数か:initform
フォームによって設定する際に使用されます。
method-combinationはstandard
が使用されます。
引数は次のような順番で受け取ります。
初期化されるインスタンス、インスタンスのアクセス可能なスロット名の集合、
そして任意の長さの初期化引数です。
最初2つよりあとの引数は、初期化引数リストの形にしなければなりません。
shared-initialize
の2番目の引数は、下記のどちらかに従います。
引数はスロットの名前のリスト(空リストでも可)であり、 スロット名の集合を指定したものです。
引数はシンボル
t
であり、すべてのスロットの集合を指定したものです。
システムが提供しているshared-initialize
のメソッドでは、
第一引数の特定パラメーターがstandard-object
クラスのものが存在します。
このメソッドは共有か局所かに関わらず、各スロットに対して次の振る舞いを行います。
もし初期化引数リスト中の初期化引数がスロットへの値を特定した場合は、 メソッドが実行する前に対象のスロットにすでに値が格納されていても、 スロットへ特定した値が格納されます。 影響があるスロットは、
shared-initialize
の第二引数で指定されたスロットとは独立しています。第二引数によって指定されたどんなスロットも、 この時点においてまだ
unbound
であった場合は、:initform
フォームに従って初期化されます。:initform
フォームを持つどのスロットも、 フォームはdefclass
宣言のレキシカル環境にて評価され、 その結果がスロットに格納されます。 例えば、before
メソッドがスロットへ値を格納する場合、:initform
フォームはスロットへの値の格納には使用されないでしょう。 もし第二引数が指定した名前が、 インスタンスのアクセス可能なスロットに対応していなかった場合は、 結果は定義されていません。この規則は7.1.4 初期化引数に従います。
ジェネリック関数shared-initialize
は、
システムが提供するメソッドreinitialize-instance
,
update-instance-for-different-class
, update-instance-for-redefined-class
,
そしてinitialize-instance
によって呼び出されます。
このようにメソッドは
これらすべてのコンテキスト上で実行できるようなアクションを指定するように、
shared-initialize
を記述することができます。
7.1.6 Initialize-Instance
ジェネリック関数initialize-instance
は、
新しく作成されたインスタンスを初期化するために、
make-instance
によって呼び出されます。
method-combinationはstandard
が使われます。
initialize-instance
のメソッドは、
単純に初期値をスロットに指定できないような初期化を実行するために定義できます。
初期化中では、次に示したアクションを実行したあとにinitialize-instance
が呼び出されます。
デフォルト初期化引数リストは、 提供された初期化引数リストと各クラスのデフォルト初期化引数を結びつける計算がされます。
デフォルト初期化引数リストの有効性はチェックされます。 もしどの初期化引数の有効として宣言されていなかった場合は、エラーが発せられます。
新しいインスタンスはスロットが
unbound
として作成されます。
ジェネリック関数initialize-instance
は
新しいインスタンスとデフォルト初期化引数とともに呼び出されます。
システムが提供するinitialize-instance
のメソッドでは、
特定パラメーターはstandard-object
クラスのものが存在します。
このメソッドは、ジェネリック関数shared-initialize
を呼び出し、
初期化引数に対応したものか、
あるいは:initform
フォームに対応した値を設定します。
ジェネリック関数shared-initialize
の引数は、
インスタンス、t
、デフォルト初期化引数を指定して呼び出されます。
注意として、initialize-instance
はデフォルト初期化引数リストを
shared-initialize
の呼び出し時に提供します。
そして最初のステップとして、
システムが提供するshared-initialize
のメソッドは、
make-instance
呼び出し時に提供された初期化引数と、
デフォルト初期化引数リストの両方を集計して呼び出されます。
initialize-instance
のメソッドは、
インスタンスの初期化時に、特定のアクションを定義することができます。
もしinitialize-instance
のafter
メソッドだけが定義された場合、
これらはシステムが提供した初期化後に実行されます。
したがってこれらは、initialize-instance
の標準的な動作には干渉しないでしょう。
オブジェクトシステムは、initialize-instance
メソッドの構築に便利な2つの関数を提供しています。
関数slot-boundp
は、スロットが値を持っているかどうかを示すbool
値を返却します。
これはinstance-initialize
のafter
メソッドを記述する際に、
まだ初期化されていないスロットのみを初期化するような仕組みを提供します。
関数slot-makunbound
は、スロットの値を削除します。
7.1.7 Make-InstanceとInitialize-Instanceの宣言
ジェネリック関数make-instance
は、最適化を考えない場合は、
下記に示す宣言のように実行されます。
(defmethod make-instance ((class standard-class) &rest initargs) ... (let ((instance (apply #'allocate-instance class initargs))) (apply #'initialize-instance instance initargs) instance)) (defmethod make-instance ((class-name symbol) &rest initargs) (apply #'make-instance (find-class class-name) initargs))
make-instance
の定義で省かれているコードは、
initargs
をデフォルト初期化引数によって指定する部分であり、
また初期化引数の結果を初期化引数に設定するかどうか決定するために、
スロットに値が設定されておらず、
メソッドの引数として供給もされていないかどうかをチェックする部分となります。
ジェネリック関数initialize-instance
は、最適化を考えない場合は、
下記に示す宣言のように実行されます。
(defmethod initialize-instance ((instance standard-object) &rest initargs) (apply #'shared-initialize instance t initargs)))
これらのコードはカスタマイズ可能です。
プログラマーへのインターフェイスレベルとしてカスタマイズできるものは、
defclass
のオプションである、:initform
, :initarg
そして:default-initargs
が含まれますし、
同様にmake-instance
, allocate-instance
, そしてinitialize-instance
のメソッド宣言があげられます。
shared-initialize
のメソッドを定義することも可能です。
この関数は、ジェネリック関数のreinitialize-instance
, update-instance-for-redefined-class
,
update-instance-for-defferent-class
, そしてinitialize-instance
によって実行されます。
メタオブジェクトレベルでは、追加でカスタマイズをサポートします。
処理系は、initialize-instance
とshared-initialize
について明確な最適化を許容しています。
7章にあるshared-initialize
の定義では、可能な最適化についての説明があります。
7.2 インスタンスのクラスの変更
関数change-class
は、
インスタンスのクラスを現在のクラスCfrom
から違うクラスCto
へ変更する際に使用します。
この関数はインスタンスの構造を変化させて、
クラスCto
の定義へ適用させるような変更を行います。
インスタンスのクラスの変更は、
スロットへの追加と削除が行われるでしょう。
インスタンスのクラスの変更は、関数eq
により定義されたものとの同一性を変更しません。
関数change-class
がインスタンスに対して実行されたとき、
2つの手順により更新が行われます。
最初の手順は、新しい局所スロットの追加と、
新しいバージョンには定義されない局所スロットの削除によるインスタンスの構造の変更です。
二番目の手順は、新しく追加された局所スロットの初期化と、
他にユーザーが定義したアクションの実行です。
これら2つの手順は、次に示す2つの章によって定義されています。
7.2.1 インスタンスの構造の修正
インスタンスをクラスCto
へ修正するために、
クラスCto
には定義されているがCfrom
には定義されていない局所スロットは追加され、
クラスCto
には定義されていないがCfrom
には定義されている局所スロットは削除されます。
クラスCto
とクラスCfrom
の両方に定義されている局所スロットの値は保持されます。
もし局所スロットがunbound
であった場合は、unbound
のままです。
クラスCfrom
では共有スロットであり、Cto
では局所スロットとして定義された
スロットの値は保持されます。
最初の更新ステップでは、どの共有スロットの値も影響がありません。
7.2.2 新しく追加された局所スロットの初期化
更新の二番目の手順では、新しく追加されたスロットを初期化し、
ユーザー定義のアクションを実行します。
このステップは、ジェネリック関数update-instance-for-different-class
によって定義されます。
ジェネリック関数update-instance-for-different-class
は、
最初の更新手順が完了したあとに、change-class
によって実行されます。
ジェネリック関数update-instance-for-different-class
は、
change-class
によって計算された引数により実行されます。
最初の引数は、更新されるインスタンスのコピーであり、
クラスCfromの
インスタンスです。
このコピーは、ジェネリック関数change-class
に動的エクステントとして保有されます。
二番目の引数は、change-class
によって更新されるインスタンスであり、
クラスCto
のインスタンスです。残りの引数は、初期化引数リストです。
システムが提供するupdate-instance-for-different-class
メソッドは、
2つの特定パラメーターがあり、
どちらもstandard-object
クラスです。
最初、このメソッドは、初期化引数の有効性をチェックし、
もし指定された初期化引数が有効であると宣言されていなかった場合は、
エラーが発せられます(詳細は7.1.2 初期化引数の有効性の宣言を参照)。
それから、このメソッドはジェネリック関数shared-initialize
を、
次に示す引数とともに呼び出します。
引数は、新しいインスタンス、新しく追加されるスロット名のリスト、
そして受け取った初期化引数です。
7.2.3 インスタンスのクラスの更新のカスタマイズ
update-instance-for-different-class
のメソッドは、
インスタンスを更新するとき、
特定のアクションを定義することができます。
もしupdate-instance-for-different-class
にafter
メソッドのみが定義された場合は、
これはシステムが提供する初期化のメソッドのあとに実行されます。
よってupdate-instance-for-different-class
の標準的な動作には干渉しないでしょう。
shared-initialize
のメソッドは、クラスの再定義を
カスタマイズするために定義されるでしょう。
詳細は7.1.5 Shared-Initializeを参照。
7.3 インスタンスの再初期化
ジェネリック関数reinitialize-instance
は、
初期化引数に従ってスロットの値を変更するときに使用されます。
再初期化のプロセスにより、スロットの値が変更され、
ユーザーが定義するアクションが実行されます。
これはスロットの追加と削除といったインスタンスの構造の修正は行いません。
また、:initform
フォームを使ったスロットの初期化を行いません。
ジェネリック関数reinitialize-instance
は、直接呼び出されるでしょう。
これは引数に一つのインスタンスが要求されます。
またreinitialize-instance
かshared-initialize
によって使用される、
任意の数の初期化引数を受け取ります。
要求されるインスタンスの引数より後の引数は、初期化引数リストの形式でなければなりません。
システムが提供するreinitialize-instance
のメソッドは、
特定パラメーターにstandard-object
クラスを取ります。
最初、メソッドは初期化引数の有効性をチェックし、
もし指定された初期化引数が有効であると宣言されていなかった場合は、
エラーが発せられます(詳細は7.1.2 初期化引数の有効性の宣言を参照)。
このメソッドは、ジェネリック関数shared-initialize
を、
次に示す引数とともに呼び出します。
引数は、インスタンス、nil
、そして受け取った初期化引数です。
7.3.1 再初期化のカスタマイズ
メソッドreinitialize-instance
は、インスタンスを更新するとき、
特定のアクションを定義することができます。
もしreinitialize-instance
にafter
メソッドのみが定義された場合は、
メソッドはシステムが提供する初期化のメソッドのあとに実行されます。
よってreinitialize-instance
の標準的な動作に干渉しないでしょう。
shared-initialize
のメソッドは、クラスの再定義を
カスタマイズするために定義されるでしょう。
詳細は7.1.5 Shared-Initializeを参照。
7.4 メタオブジェクト
オブジェクトシステムの実装は、クラス、メソッド、そしてジェネリック関数を扱います。 オブジェクトシステムは、メソッドとクラスによって定義された、 ジェネリック関数の集合を含みます。これらのジェネリック関数の振る舞いは、 オブジェクトシステムの振る舞いを定義します。 これらのメソッドが定義されているクラスのインスタンスは、メタオブジェクトと呼ばれます。
7.4.1 標準メタオブジェクト
オブジェクトシステムは、標準メタオブジェクトと呼ばれる
メタオブジェクトの集合を提供します。
これらはstandard-object
クラスと、
standard-method
, standard-generic-function
, method-combination
の
それぞれのクラスのインスタンスを含みます。
standard-method
クラスは、defmethod
とdefgeneric
フォームによって 定義されるメソッドの標準クラスです。standard-generic-function
クラスは、defmethod
,defgeneric
,defclass
のフォームによって 定義されるジェネリック関数の標準クラスです。standard-object
という名前のクラスは、standard-class
クラスのインスタンスです。 またstandard-object
は、自分自身とstructure-class
を除く、standard-class
のインスタンスである全てのクラスのスーパークラスです。
すべてのmethod-combinationオブジェクトは、
method-combination
クラスのサブクラスのインスタンスです。
7.5 スロット
7.5.1 スロットの紹介
standard-class
がメタクラスのオブジェクトは、
0個かそれ以上の名前の付いたスロットを持ちます。
オブジェクトのスロットは、オブジェクトのクラスによって決められます。
各スロットは、値を保有できます。
スロットの名前は、変数名として使うのに有効な構文のシンボルです。
スロットが値を持っていないときは、そのスロットはunbound
であると言われます。
もしunbound
のスロットを読み込んだ場合は、
ジェネリック関数のslot-unbound
が呼び出されます。
システムが提供するslot-unbound
のメソッドでは、
引数の特定パラメーターがt
クラスのものが提供されており、エラーが発せられます。
もしslot-unbound
が値を返却する場合は、
第一返却値はスロットの値として、そのときに使用されるものとなります。
スロットのデフォルト値フォームは、
スロットオプション:initform
によって定義されます。
:initform
フォームに値が提供された場合は、
defclass
が評価された中のレキシカル環境にてフォームが評価されます。
defclass
が評価された中のレキシカル環境に沿った:initiform
のことを、
補足された初期化フォームと呼びます。
詳細は7.1 オブジェクトの作成と初期化を参照。
局所スロットとして定義されたスロットは、 正確に一つのインスタンスがアクセス可能です。 すなわち、唯一つのスロットが確保されます。 共有スロットとして定義されたスロットは、 クラスとそのサブクラスによって与えられる、複数のインスタンスから見ることができます。
defclass
フォームによるクラスが、スロット特定子に名前を含んでいたとき、
クラスは名前が与えられたスロットが定義されたと言います。
局所スロットの宣言では、即座にはスロットが作成されません。
なぜならクラスのインスタンスが作成されるときに、スロットが作成されるからです。
共有スロットは宣言では、即座にスロットを作成します。
defclass
のスロットオプション:allocation
は、スロットの定義時に種類を指定します。
もしスロットオプション:allocation
の値が:instance
ならば、局所スロットが作成されます。
もしスロットオプション:allocation
が:class
ならば、共有スロットが作成されます。
もしスロットがインスタンスのクラスによって定義された場合、 あるいはクラスのスーパークラスから継承された場合は、 スロットはクラスのインスタンスからアクセス可能であると言います。 インスタンスからは、せいぜい一つの名前付きスロットがアクセス可能です。 クラスによって定義された共有スロットは、 クラスのすべてのインスタンスからアクセス可能です。 スロットの継承による詳細な説明は、7.5.3 スロットの継承とスロットオプションを参照。
7.5.2 スロットへのアクセス
スロットは次の2つの方法にてアクセスできます。
関数slot-value
を使用する方法、
そしてdefclass
フォームによって生成されるジェネリック関数を使用する方法です。
関数slot-value
は、
defclass
フォームで対象のクラスのインスタンスにて
アクセス可能に設定したスロットに対しては、
どんなスロットの名前でも指定してアクセスすることができます。
マクロdefclass
は、スロットの読み書きをするメソッドを生成するための構文が提供されています。
もしreader
のメソッドが要求された場合、
スロットの値を読むためのメソッドが自動的に生成されますが、
ただし値を格納するためのメソッドは生成されません。
もしwriter
のメソッドが要求された場合、
スロットの値を書き込むためのメソッドが自動的に生成されますが、
ただし値を読み込むためのメソッドは生成されません。
もしaccessor
のメソッドが要求された場合は、
スロットの値を読むためのメソッドと、
スロットの値を書き込むためのメソッドが自動的に生成されます。
reader
とwriter
のメソッドは、slot-value
を使用して実装されます。
スロットに対してreader
かwriter
のメソッドを指定するときは、
ジェネリック関数の名前とそれに沿って生成されるメソッドの名前を直接指定します。
もしwriter
メソッドの名前をシンボルname
に指定した場合、
スロットへ書き込むためのジェネリック関数の名前がシンボルname
となり、
そのジェネリック関数の引数は、
新しい値、インスタンスの順に2つ取ります。
もしaccessor
メソッドの名前をシンボルname
に指定した場合、
スロットから読み込むためのジェネリック関数の名前がシンボルname
となり、
そしてスロットへ書き込むためのジェネリック関数の名前が、リストの(setf name)
となります。
スロットオプションの:reader
, :writer
, :accessor
の指定により、
作成か修正が行われたジェネリック関数は、
正確に普通のジェネリック関数として扱うことができます。
注意として、slot-value
はスロットから値を読み込むか書き込む時に使われますが、
そのスロットのreader
かwriter
のメソッドが存在するかどうかに関わらず使用できます。
slot-value
が使われる時、reader
かwriter
のメソッドは実行されません。
マクロwith-slots
は、
指定されたスロットがレキシカル変数として使えるようにするレキシカル環境を確立します。
マクロwith-slots
は、指定したスロットへアクセスするために関数slot-value
を実行します。
マクロwith-accessors
は、
指定したスロットがレキシカル変数としてスロットのaccessor
を通して
使えるようにするレキシカル環境を確立します。
マクロwith-accessors
は、指定したスロットへアクセスするために、
適切なaccessor
を実行します。
7.5.3 スロットの継承とスロットオプション
クラスC
のインスタンスについて、アクセス可能な全てのスロットの名前の集合は、
クラスC
とそのスーパークラスによって定義されたスロットの名前の集合の和集合となります。
インスタンスの構造は、そのインスタンスの局所スロットの名前の集合です。
単純な場合として、たった1つのクラスC
と、そのスーパークラスにて、
名前ありのスロットを定義したとします。
もしスロットがC
のスーパークラスによって定義された場合、
そのスロットは継承されたと言えます。
スロットの特性は、クラス定義のスロット指定子によって決定されます。
スロットS
を定義したクラスを考えます。
もしスロットオプション:alocation
が:instance
ならば、S
は局所スロットであり、
C
の各インスタンスはS
と名前の付いた独自のスロットをもち、
S
には独自の値が格納されます。
もしスロットオプション:allocation
が:class
ならば、
S
は共有スロットであり、S
が定義されたクラスに値が格納されます。
そしてC
の全てのインスタンスは、その1つのスロットにアクセスできます。
もしスロットオプション:allocation
が省略された場合は、:instance
が使用されます。
一般的に、複数のクラスである、C
とそのスーパークラスは、
1つの名前付きのスロットを定義できます。
そのような場合、C
のインスタンスに対しては、
与えられた名前に対してただ1つのスロットがアクセス可能です。
そしてそのスロットの特性は、いくつかのスロットの指定子を
計算によって結び付けたものになります。
計算方法を次に示します。
1つのスロットの名前に対する全てのスロット指定子は、 クラス
C
のクラス優先順位リストのクラスに従って、 最も特定的なものからそうでないものへ順序付けられます。 どのスロット指定子の直下に特定されるかの全ての参照は、この順序に従って調査されます。スロットの確保は、最も特定的なスロット指定子によって制御されます。 もし最も特定的なスロット指定子がスロットオプション
:allocation
を含んでいなかった場合は、:instance
が使用されます。 特定的ではないスロット指定子は、確保には影響しません。スロットのデフォルト初期値フォームは、 スロットオプション
:initform
が含まれる最も特定的なスロット指定子の、:initform
の値になります。 もしスロット指定子が:initform
を持っていなかった場合、 スロットはデフォルト初期値フォームを持ちません。スロットの値は、常に型
(and T1 ... Tn)
となるでしょう。 ここでT1 ... Tn
とは、全てのスロット指定子が持っているスロットオプション:type
の値です。 もしスロット指定子がスロットオプション:type
を持っていなかった場合は、 スロットの値は常に型t
となります。 スロットの型に合っていない値をスロットに格納しようとした結果については未定義です。指定したスロットを初期化する際に使用する初期化引数の集合は、 全てのスロット指定子の中のスロットオプション
:initarg
で定義された初期化引数の和集合です。スロットのドキュメント文字列は、 スロットオプション
:documentation
が含まれる最も特定的なスロット指定子の、:documentation
の値になります。 もしスロット指定子が:documentation
を持っていなかった場合、 スロットはドキュメント文字列を持ちません。
スロットの確保の規則では、共有スロットはシャドウすることができます。
例えば、もしクラスC1
が、スロットの名前S
、
スロットオプション:allocation
が:class
のスロットを定義した場合、
そのスロットは、C1
とその全てのサブクラスのインスタンスからアクセス可能です。
しかし、もしC2
がC1
のサブクラスであり、C2
が名前S
のスロットを定義した場合、
C2
とその全てのサブクラスのインスタンスでは、C1
のスロットは共有されません。
クラスC1
が共有スロットを定義したときは、
次の条件の時にC1
のどんなサブクラスC2
でもその単一のスロットは共有されます。
それは、C2
のdefclass
フォームで同じ名前のスロットを定義していないとき。
あるいは、C2
のクラス優先リスト内において
同じ名前のスロットを定義しているクラスを見たとき、
C1
よりも先導しているものがC2
のスーパークラスに存在していない場合です。
型の規則による結果は、スロットの値が関連するスロットの 各スロット指定子の型の条件を満たすことです。 スロットの型の条件が守られていない値を スロットに格納しようとした際の結果は未定義なので、 スロットの値は型の条件の安全性を失うでしょう。
スロットオプション:reader
, :writer
, :accessor
は、
スロットの特性を宣言すると言うよりは、
メソッドを作成するものです。
reader
とwriter
メソッドは、
7.6.7 メソッドの継承で説明される定義により継承されます。
スロットにアクセスするメソッドは、スロットの名前と、 スロットの値の型のみを使用します。 例えば、スーパークラスが、 指定した名前により共有スロットにアクセスすることを期待するメソッドを提供した場合、 またサブクラスが同じ名前で局所スロットを定義した場合を考えます。 もしスーパークラスによって提供されたメソッドを、 サブクラスのインスタンス上で使用した場合、 メソッドは局所スロットにアクセスします。
7.6 ジェネリック関数とメソッド
7.6章は7章Objectsのジェネリック関数関連の和訳を参照。