メタクラス

オブジェクト指向プログラミングにおいてメタクラスとは、インスタンスがクラスとなるクラスのことである。通常のクラスがそのインスタンスの振る舞いを定義するように、メタクラスはそのインスタンスであるクラスを、そして更にそのクラスのインスタンスの振る舞いを定義する。全てのオブジェクト指向プログラミング言語でメタクラスが利用できるわけではない。利用できるものの中でもクラスの振る舞いが定義できる範囲は様々である。各言語はそれぞれ独自のメタオブジェクトプロトコルを備えている。メタオブジェクトプロトコルとは、クラスそのものの挙動をもオブジェクト指向のルールで記述し、初期化やインスタンス化のルール、実行状態の管理などをカスタマイズする機構である。Smalltalk、Common Lispが代表的である。

1. Pythonでの例
------------

Pythonの組み込みクラス type はメタクラスである。次に示す単純なPythonのクラスについて説明する。

class Car:__slots__ = ['make', 'model', 'year', 'color']

def __init__: self.make = make self.model = model self.year = year self.color = color

@property def description: """ このCarの説明を返す """ return "%s %s %s %s" % <WtXmlEndTag />

このコードを実行した時、Car は type のインスタンスになっている。上記の Car クラスのソースコードには __init__ メソッドが Car のインスタンスが生成されるたびに呼ばれるといった細々としたことは記述されていない。メタクラスが用意されていない言語ではこのような振る舞いは言語仕様で定義されており、変更することは不可能である。Pythonではメタクラス type がこれらの動作を決定しており、type の代わりに違うメタクラスを使用することでこれらの振る舞いを変更することが可能である。

上に示した例は4つの属性 make と model、year、color の辺りが冗長である。メタクラスを使えば、この冗長さを取り除くことが可能である。Pythonではメタクラスは type のサブクラスとして定義するのが最も簡単である。 class AttributeInitType: def __call__: """ 新しいインスタンスを生成する """

普通にインスタンスを生成するobj = type.__call__

生成したインスタンスの属性を設定するfor name in kwargs: setattr

生成したインスタンスを返すreturn obj<WtXmlEndTag /> このメタクラスはインスタンスの生成を上書きしているだけで、他の機能はまだ type が処理している。

さて、このメタクラスを用いて Car クラスを書き直すことが可能である。Python 2ではクラス定義中で __metaclass__ にこのメタクラスを代入すればよい。 class Car:__metaclass__ = AttributeInitType__slots__ = ['make', 'model', 'year', 'color']

@property def description: """ Return a description of this car. """ return "%s %s %s %s" % <WtXmlEndTag /> これで、Car のインスタンスを次のように生成できる。 cars = [ Car, Car]

2. Smalltalkにおけるメタクラス
---------------------

Smalltalkでは全てがオブジェクトである。また、Smalltalkはクラスベースであるため、オブジェクトは必ず、そのオブジェクトの構造や、そのオブジェクトがどんなメッセージを理解するのかを定義したいずれかのクラスに属さなければならない。このルールにおいてクラスも例外ではなく、Smalltalkではクラスもオブジェクトであり、同時に別のクラスのインスタンスでもある。このクラスのクラス、つまりあるクラスが属する特殊なクラスが「メタクラス」である。

例えば、「自動車」クラスCarから生成されたオブジェクトaCarが、Carのインスタンスであり、Carというクラスに属しているのと同じように、クラスCar自身にも自らが属しているクラス、つまりメタクラスが存在する。原則としてSmalltalkでメタクラスは無名扱いだが、便宜的にクラス名にclassを付して呼称する慣習がある。Carなら、そのメタクラスはCar classである。なおこの表記はあるオブジェクトが属するクラスを第一級オブジェクトとして得るためのSmalltalk式としての意味も併せ持つ。

クラスメソッドは、通常、メタクラスに定義されている。これはインスタンスメソッドがクラスに定義されているのと考え方は同じである。

インスタンスメソッドの場合、たとえば整数の「2」というオブジェクトに何らかのメッセージを送ると、2が属するSmallIntegerというクラスから対応するメソッドがないか探し始める。SmallIntegerに見つからなければそのスーパークラスであるIntegerで…というようにスーパークラスを次々と手繰ってゆき、最後のObjectというルートクラスまで探索を続ける。

クラスメソッドの場合も考え方はほとんど変わらない。たとえばSmallIntegerというクラスに対してメッセージを送ると、メソッド検索はそのクラスであるSmallInteger classから開始される。そしてインスタンスメソッドの場合と同様に、SmallInteger classのスーパークラスであるInteger class、さらにそのスーパークラスを次々と手繰り、Object classまで探索を続ける。なおSmalltalkにおいては、メタクラスの継承関係は原則としてそのインスタンスであるクラスの継承関係と一致する。つまりSmallIntegerのスーパークラスがIntegerなら、そのメタクラス同士も同じ関係、すなわち、SmallInteger classのスーパークラスはInteger classである。ただしObject classだけは例外で、Objectのスーパークラスが未定義であるのに対し、Object classのスーパークラスはClassと定められている。したがって、クラスへのメッセージ送信に伴うメソッド探索はObject classでは終了せず、クラスとしての振る舞いを定めたClassとそのスーパークラスパスにある二つのクラスを経てObjectに行き着くまで続行される。同時にこのことはSmalltalkにおいてクラスもオブジェクトである、すなわちクラスObjectのインスタンスであり、通常のクラスのインスタンス同様Objectに定義されたメソッドをコールできることの理由をうまく説明している。

初期のSmalltalkには、同じくClassと名付けられたたったひとつのメタクラスしか用意されていなかった。つまりこのことは、すべてのクラスはClassのインスタンスであり、他のクラスと同じ共通のメソッドしか持つことができなかったということを意味する。その後、クラスが独自のメソッドやインスタンス変数を持てるように、Smalltalk-80では改めてクラスそれぞれが固有のメタクラスが生成されるように拡張され今のかたちになった。

クラスのクラスであるメタクラスは、インスタンスのクラスである通常のクラスほどには独自性を要求されないので、すべてのメタクラスはMetaclassという名のひとつクラスのインスタンスとして位置づけられている。ちなみにMetaclassのメタクラスはMetaclass classだが、同時にこれは前述のルールに則りMetaclassのインスタンスでもある。このトリックによって、さらにメタ、さらにそのメタ…と永遠に繰り返さずにも済むようになっている。

メタクラスはクラス生成時に同時に生成される。より具体的には、あるクラスに対して、そのサブクラスとして例えばFooを生成するように命ずると、まず暗黙のうちにFoo classが生成され、そのインスタンスとしてFooが生成される。先にも述べたがメタクラスは無名であり、クラスブラウザのクラス一覧にも現れえない。しかし、メタクラスが必ずクラスとペアで生成されることを利用して、クラスブラウザではclassボタンを押して表示を切り替えることで対応するメタクラスの定義にアクセスしたりその内容を編集可能になっている。

クラスおよびメタクラスの振る舞いは次の4段階の継承階層を経て定義されている。

Object - インスタンス・クラスの別なく、オブジェクトとしての普遍的な振る舞いBehavior - クラスの最低限の振る舞いClassDescription  - クラスの振る舞いClass - 通常のクラスの振る舞いMetaclass - メタクラス独自の振る舞い

3. Rubyにおけるメタクラス
----------------

Rubyでは、クラス自体がClassというクラスのインスタンスであり、Class.newという形でクラスを動的に生成することもできる。ClassはModuleを継承しており、動的なメソッドの操作といったクラスに共通な機能があるほか、特異メソッドを使うことで、Smalltalk-76のような単一のメタクラスでクラスごとの多様性をもたせることができる。 具体的には、存在しないメソッドの呼び出しや、継承・メソッドの追加といったタイミングで呼び出されるフック関数を特異メソッドとして実装することができる。

4. 利用可能な言語とツール
--------------

有名なプログラミング言語でメタクラスが利用可能なものは以下である。

Common Lisp
Groovy
Object Pascal 
Objective-C
Ruby
Python
Perl
Smalltalk次のようにそれほど普及していない言語の中にはメタクラスが利用できるものもある。いくつかは研究目的であり、1990年代初頭にまで遡る: OpenJavaやOpenC++、OpenAda、CorbaScript、ObjVLisp、Object-Z、MODEL-K、XOTcl、MELDCなど。

また、Prologのオブジェクト指向拡張であるLogtalkでもメタクラスが利用できる。

さらに、Resource Description Framework とUnified Modeling Language は両者共にメタクラスが利用できる。

5. 関連項目
-------

メタモデル
メタプログラミング
リフレクション
Adapter パターン

6. 出典
-----

7. 参考文献
-------