コンテンツにスキップ

「メタクラス」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
G000001 (会話 | 投稿記録)
oldid=86393412 の記述は言語仕様からすると間違った記述が多いのに加えて、独自の意味を持たせた単語が多いため解釈が曖昧になるような表現が多いのですが検証しようにも出典がないので修正が難しくやむなく削除しました。
タグ: 2017年版ソースエディター
Poshnegev (会話 | 投稿記録)
(同じ利用者による、間の4版が非表示)
1行目: 1行目:
[[オブジェクト指向]][[プログラミング (コンピュータ)|プログラミング]]において'''メタクラス'''とは、インスタンスが[[クラス (コンピュータ)|クラス]]なるクラスのことである。通常のクラスがそのイスタンスの振る舞いを定義すように、メタクラスはそのインスタンスであるクラスを、そして更にのクラスのインスタンスの振る舞いを定義する。全てのオブジェクト指向プログラミング言語メタクラスが利用できわけではない。利用できるものの中でもクラスの振る舞いが定義できる範囲は様々である。各言語はそれぞれ独自のメタオブジェクトプロトコル(MOP)備えている<ref>{{cite book|author=Ira R. Forman and Scott Danforth|title=Putting Metaclasses to Work|year=1999|isbn=0-201-43305-2}}</ref>。メタブジェクトプロトコルとは、クラスそのものの挙動をもオブジェクト指向のルルで記述し、初期化やインスタンス化のルル、実行状態の管理などをカスタマズする機構。[[Smalltalk]][[Common Lisp]]([[CLOS]])が代表的である。
'''メタクラス'''(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:Smalltalk 80 metaclasses.svg|thumb|Smalltalk-80でのメタクラスの継承関係を示すUML]]
[[File:Smalltalk_metaclass.png|リンク=https://en.wikipedia.org/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>を継承している。
== Rubyにおけるメタクラス ==

Rubyでは、クラス自体がClassというクラスのインスタンスであり、<code>Class.new</code>という形でクラスを動的に生成することもできる<ref>『[[#フラナガン&まつもと2009|プログラミング言語 Ruby]]』、p.278</ref>。ClassはModuleを継承しており、動的なメソッドの操作といったクラスに共通な機能があるほか、特異メソッド(あるオブジェクトだけに有効なメソッド)を使うことで、Smalltalk-76のような単一のメタクラスでクラスごとの多様性をもたせることができる。
<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でのメタクラス ==
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]]を用いて)
*[[Common Lisp]]([[Common Lisp Object System|CLOS]]を用いて)
* [[Groovy]]
* [[Object Pascal]] (中でも[[Delphi]]が代表的である)
* [[Objective-C]]
* [[Objective-C]]
*[[Object Pascal]] (中でも[[Delphi]]が代表的である)
* [[Ruby]]
* [[Ruby]]
* [[Python]]
* [[Python]]
* [[Perl]](メタクラスプラグマまたはMooseを用いて)
* [[Perl]](メタクラスプラグマまたはMooseを用いて)
* [[Smalltalk]]
* [[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年代初頭にまで遡る<ref>[http://csl.ensm-douai.fr/noury/uploads/1/nouryBouraqadi.Gpce03WorkshopOnRepls.pdf An implementation of mixins in Java using metaclasses]</ref>: [[OpenJava]]や[[OpenC++]]、[[OpenAda]]、[[CorbaScript]]、[[ObjVLisp]]、[[Object-Z]]、[[MODEL-K]]、[[XOTcl]]、[[MELDC]]など。
ややマイナーな言語の中にメタクラスが利用できるものある。いくつかは研究目的であり、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)は、オブジェクト指向プログラミングにおいて、そのインスタンスクラスになるクラスを指す。つまりクラスのクラスである。通常のクラスがオブジェクトの振る舞いを定めるのと同様に、メタクラスはクラスの振る舞いを定めて、それはそのインスタンスにも反映される。全てのオブジェクト指向言語がメタクラスをサポートしている訳ではない。クラスの振る舞いをオーバーライドできるという共通見解以外の特徴・性質・役割は、言語別に様々である。

SmalltalkCommon Lisp(CLOS)、Rubyなどでは、各メタオブジェクト英語版継承実体化(後述)の連鎖で構成される末端物が、通常のプログラマが用いるクラスのメタクラスになっている。そのメタクラスは、クラスとその他メタ要素を第一級オブジェクトとして扱えて組み立てられるオブジェクトである。メタオブジェクト・メタクラス・クラス・オブジェクトの相互関係・相互作用・相互組立の規則集は、メタオブジェクトのプロトコル(MOP)と呼ばれる[1]

他の言語では、メタオブジェクトとそのプロトコルはシステム内封印されていることが多い。Pythonではクラスの振る舞いを修正できる組み込みクラスがメタクラスと呼ばれている。JavaC#では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)

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

上に示した例は4つの属性 makemodelyearcolor の辺りが冗長である。メタクラスを使えば、この冗長さを取り除くことが可能である。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 classObjectクラスのメタクラス)まで探索を続ける。なおSmalltalkにおいては、メタクラスの継承関係は原則としてそのインスタンスであるクラスの継承関係と一致する。つまりSmallIntegerのスーパークラスがIntegerなら、そのメタクラス同士も同じ関係、すなわち、SmallInteger classのスーパークラスはInteger classである。ただしObject classだけは例外で、Objectのスーパークラスが未定義(nil)であるのに対し、Object classのスーパークラスはClassと定められている。したがって、クラスへのメッセージ送信に伴うメソッド探索はObject classでは終了せず、クラスとしての振る舞いを定めたClassとそのスーパークラスパスにある二つのクラス(ClassDescriptionBehavior。後述)を経て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-80のMOPのUML図

Smalltalkのメタオブジェクトプロトコルは(右図参照)(A)継承ルーツ(図左側の4×2ブロック)と、(B)インスタンス化ルーツの(図右側の縦5ライン)に分けて解釈できる。

(A)では、Object classが、Objectの子孫クラスClassを継承して関係ループしている(上位型がその下位型の値を継承している)。これはLambda-P依存型に相当し、即ちSmalltalkのMOPは述語論理に準拠している。従ってそこでの継承必要条件十分条件の関係で、インスタンス化存在記号全称記号の関係で考えると分かりやすくなる。

(B)では、MetaclassMetaclass classのインスタンス化関係がループしている。また、MetaclassObjectの子孫クラスClassDescriptionを継承している。

Objectはあらゆるユーザー定義クラスのスーパークラスになり、Metaclassはあらゆるユーザー定義クラスのメタクラスになる。そこまでの継承関係を下に示す。

  1. Object - 全オブジェクト共通のデフォルト振る舞いの情報
  2. Behavior - コンパイル済みメソッドとオブジェクトの生成/実行の情報、オブジェクト分化の最低限
  3. ClassDescription - ClassMetaclassから見た抽象クラス。クラス変数の名前とコメントの情報
    • Class - Objectの大元スーパークラス性質はここから
    • Metaclass - クラス変数の初期化、インスタンス生成メッセージの情報

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までの継承関係を下に示す。

  1. T - 全オブジェクトと全関数の大元
  2. standard-object - 全オブジェクトの大元スーパークラス
  3. metaobject - ここからspecializer型特定子、スロット定義、総称関数、メソッド、メソッド結合に分化される。抽象クラス
  4. specializer - 型特定の情報。抽象クラス
  5. class - ここからstandard-class標準クラス、組込クラス、関数クラス、転送クラスに分化される。抽象クラス
  6. standard-class - ユーザー定義クラスを定義できる

Rubyでのメタクラス

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

緑=継承関係、青=メタクラス関係、C=Class、A・B=クラス、u・v=オブジェクト

RubyのMOPは、継承やインスタンス化のループ関係がない平易なものであり、BasicObject発のトップダウンの継承ツリー構造になっている。だが、特異クラスの方はやや複雑になる。(右図参照)Rubyの特異クラス(eigenclassとも)は、Smalltalkのimplicitクラス(Foo classとか)を置き換えたものである。

まず、ユーザー定義クラスのメタクラスであるClassまでの継承関係を下に示す。

  1. BasicObject - 大元
    • Kernel - 全共通デフォルトメソッドを定義している。Objectにインクルードされる
  2. Object - 全てのクラスのスーパークラス
  3. Module - モジュールのクラス。ユーザー定義モジュールを定義できる
  4. Class - クラスのクラス。ユーザー定義クラスを定義できる

RubyではクラスAをランタイム改装(メソッド追加など)すると、その特異クラスの方が改装されてそれがクラスAに反映されるようになっている。クラスAを定義するとClass←Aの特異←Aのメタクラス関係ができるが、ここでAのサブクラスBを定義すると、Class←Bの特異←Bのメタクラス関係もできて、Aの特異←Bの特異の継承関係もできる。

利用可能な言語とツール

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

ややマイナーな言語の中にもメタクラスが利用できるものがある。いくつかは研究目的であり、1990年代初頭にまで遡る。例えば、OpenJava[7]OpenC++OpenAdaCorbaScriptObjVLispObject-ZMODEL-KXOTclMELDCなどがある。PrologのOOP拡張版Logtalkでもメタクラスが利用できる。また、Resource Description Framework (RDF)とUnified Modeling Language (UML)は両者共にメタクラスが利用できる。

関連項目

出典

  1. ^ Ira R. Forman and Scott Danforth (1999). Putting Metaclasses to Work. ISBN 0-201-43305-2 
  2. ^ IBM Metaclass programming in Python, parts 1 Archived 2008年9月3日, at the Wayback Machine., 2 and 3
  3. ^ Artima Forum: Metaclasses in Python 3.0 (part 1 of 2) (part 2 of 2)
  4. ^ David Mertz. “A Primer on Python Metaclass Programming”. ONLamp. 2003年4月30日時点のオリジナルよりアーカイブ。2006年6月28日閲覧。
  5. ^ プログラミング言語 Ruby』、p.278
  6. ^ プログラミング言語 Ruby』、pp.287-290
  7. ^ An implementation of mixins in Java using metaclasses

参考文献