defclass
(defclass name (superclass*) ((slot slot-option...) ...) class-option...)
クラスを定義し、名前シンボルを返します。これは 静的な CLOS サブセットです。すべてのスーパークラスは先行する defclass で定義済みでなければならず、インスタンスは make-instance で生成される第一級のオブジェクトです(defstruct のインスタンスと同様にリストではなく、consp は nil、print は #<NAME :SLOT value ...> と表示します)。スロットオプション:
:initarg keyword— コンストラクタで使うキーワード(省略時はスロット名のキーワード):initform expr— スロットが与えられなかったときに生成時に評価されるデフォルト値。省略するとスロットは未束縛のままになります(CL と同じ):slot-boundpはnilを返し、読み取りはunbound-slotをシグナルします:reader fn—fnを読み取り関数として定義:accessor fn—:readerと同様で、さらにsetf可能な place になります
サブクラスはスーパークラス群の全スロットを継承し、そのインスタンスは各スーパークラスに対する defmethod のクラス specializer にマッチします。多重継承をサポートします: 最初のスーパークラスのスロットが先頭に並び、以降のスーパークラスはまだ存在しない名前のスロットを追加します。ダイヤモンド継承では共有スロットは 1 つだけ保持され、メソッドディスパッチと再宣言されたスロットオプションはともにクラス優先順位リスト(CLHS 4.3.5 — クラス自身、次にスーパークラスを左から右へ、それぞれが自身のスーパークラスより先。局所順序が矛盾する場合はエラー)に従います。サブクラスは継承したスロットを再宣言できます: 記憶域は継承した 1 つのスロットのままで、サブクラスの :initform/:initarg が継承分を上書きし、reader/accessor は継承分に追加されます。reader/accessor は通常の defun なので第一級関数です。クラスオプションは (:documentation "...")(受理して無視)と (:default-initargs :initarg value ...)(make-instance が未指定の initarg に適用するデフォルト)をサポートします。スロットオプションは :reader/:accessor に加えて :type(記録のみ、チェックなし)、:writer(シンボルなら新しい値を先頭に取る 2 引数のジェネリック関数、(setf name) なら name の setf 関数を定義)、:allocation をサポートします: :allocation :class はスロットの値を、宣言したクラス・そのインスタンス・サブクラスが共有する 1 つのセルに格納します — どのアクセス経路(accessor、slot-value、make-instance の initarg)も共有セルを読み書きし、:initform はクラス定義時に一度だけ評価され、サブクラスが :allocation :class 付きで再宣言すると独自のセルを持ちます(CLHS 7.5.3)。:metaclass(後述)がない場合、その他のクラスオプションとその他のスロットオプションはエラーです。コンパイルパスでは defclass はトップレベルフォームとしてのみサポートされ、find-class と class-of はクラスメタオブジェクトを返します。実行時のクラス操作のうち存在するのは(対象は計算値でもよい)change-class だけです。既存の名前に対して defclass を評価すると定義は更新され、:metaclass 付きクラスは同一メタオブジェクトに対する reinitialize-instance 経由でクラス定義プロトコルを再実行します(後述)。ただし既存インスタンスは更新されず(update-instance-for-redefined-class は存在しません)、コンパイルパスでは静的にコンパイルされる部分(スロットレイアウト、コンストラクタ、accessor)は常に最後の定義に従います。
メタクラス(静的な MOP サブセット): クラスオプション (:metaclass M) には、先に defclass で定義された standard-class を継承するクラスを指定します。クラス定義は定義時にクラス定義プロトコルを実行します: メタクラスがインスタンス化され(その shared-initialize メソッドは、他の未知のクラスオプションを「オプションの残り部分のリスト」を値とする initarg として受け取ります。:before メソッドが宣言済み :initarg を持つスロットに書いた値は、その後に供給された initarg で上書きされます — CL の充填順です。下の table-name のように :initarg 宣言のないスロットは :before の書き込みを保持します)、各スロットの非標準オプション(:col-type など)は initarg として closer-mop:direct-slot-definition-class に渡され、その答えのクラスがそのスロットの direct-slot-definition メタオブジェクトとしてインスタンス化されます。実効スロットは closer-mop:compute-effective-slot-definition を通して計算され(デフォルトメソッドは、ユーザーのオーバーライドの call-next-method の動的スコープの内側で closer-mop:effective-slot-definition-class を選んでインスタンス化します)、closer-mop:finalize-inheritance は即時に実行されます(CL は遅延ファイナライズですが、入力は静的なので定義時エラーのタイミングだけが異なります)。以後 find-class と class-of はメタクラスのインスタンスを返し、クラス自体のインスタンスは通常のオブジェクトのままです。定義は closer-mop:ensure-class-using-class を経由します — そのユーザー :around メソッドは既存のクラスメタオブジェクトでディスパッチされるため、同じ名前が再定義されたときに発火し(初回定義では nil が渡ります)、再定義は同一メタオブジェクトに対する reinitialize-instance パスを取ります。初期化は通常の generic チェーンを通るため、メタクラス(またはスロット定義クラス)上のユーザー initialize-instance/reinitialize-instance :around メソッドは、mito のテーブルクラスが行うように、(apply #'call-next-method ...) の前に initarg(:direct-superclasses や :direct-slots を含む)を書き換えられます。スロット定義メタオブジェクトは closer-mop:slot-definition-initfunction(:initform を評価する 0 引数関数。:initform がなければ nil)も保持します。プロトコルは静的です: トップレベル以外の位置の defclass や、定義時に未知のクラスに対するプロトコル呼び出しはエラーをシグナルします。