5. クラスとモジュール
Ruby はいわゆる純粋なオブジェクト指向言語である。 厳密には議論の余地があるとはいえ,通俗的には あらゆるものがクラス (class) のインスタンスである。 すべてのクラスはモジュール (module) でもあるが, クラスでないモジュールも存在する。 そういったモジュールは直接自分自身のインスタンスを作ることができない。 モジュールは名前空間として,またはクラスへの mix-in として使われる。
クラスそれ自身はメタクラス (metaclass) である Class のインスタンスである。 モジュールそれ自身はメタクラス Module のインスタンスである。 Class は Module の派生クラスであり, Module は Object の派生クラスである。 そして Object それ自身は Class のインスタンスである。
新しいモジュールを定義するには,
module Foo end
新しいクラスを定義するには,
class Bar end
モジュール定義やクラス定義は「式」である。
ただし,結果の値は重要ではない。
定義の本体が実行され,
副作用としてモジュールやクラスへの参照を値とする定数 Foo や Bar
が定義されることが重要である。
どこに属する定数として定義されるかについては,E = 2.718281828459
など他の一般の定数定義と同じである。
例えば,トップレベルで定義したときは,Object に属する定数として
モジュール名やクラス名が定義される。
クラス定義も,一般の規則にしたがい,改行のかわりにセミコロンを使うことができる。
class Bar; end
基底クラスを指定してクラスを定義するには,
class MyString < String end
モジュールの名前空間の内側でクラスを定義するには,
module Foo class MyString end end
クラス名は定数にすぎないから, モジュール内部のクラスは二重コロン記法で参照できる。
Foo::MyString.new
メソッド呼出しにとっては,ドットと二重コロンは交換可能である。 メソッド呼出し x.f を x::f と書いてもよい。 上記の例は Foo::MyString::new と書くことができる。 一方,定数 K には二重コロンだけが使用できる (例: x::K )。 もしも x.K と書いた場合は,もっぱらメソッド呼出しと解釈される。
二重コロン記法が C++ 由来であり,クラス・メソッドが概ね C++ の静的メンバ関数に相当することから, 二重コロンを定数のほか,クラス・メソッドに使う流儀もある。
5.1 メソッドの定義
定数と同じく,メソッドはモジュールかクラスに属する。 ruby のトップレベルで定義されたメソッドは, Object の private インスタンス・メソッドとなる (したがって,トップレベル自身を含むあらゆる文脈の self に対して実行できる)。
クラス定義やモジュール定義の本体で定義されたメソッドは,普通は, そのクラスやモジュールの public インスタンス・メソッド となる (したがって,そのクラスのインスタンス,またはそのモジュールを mix-in したクラスのインスタンス, さらにその派生クラスのインスタンスに対して実行できる)。
新しいメソッドを定義するには,キーワード def を使う。
class Bar def baz end end
引数をとるメソッドを定義するには,仮引数名を与える。
def baz(a, b) end
省略可能な引数をとるメソッドを定義するには,仮引数にデフォルト値を与える。
def baz(a, b=nil) end
デフォルト値は,妥当な Ruby 式ならば何でもよいが, たいていは単純な値にする。
メソッドはいわゆる残余引数 (rest argument) をとることもできる。 残余引数は,任意の仮引数名の前に * を与えて指定する。 残余引数は,普通の仮引数との対応にあぶれた残りの実引数をすべて集めて1個の配列にする。 残余引数は,末尾の引数として与える必要がある。ただし,後述のブロック引数を除く。
def baz(a, *rest) end
のとき,メソッド呼出し baz(1, 2, 3) に対し, a は 1 になり, rest は [2, 3] になる。
メソッドを呼び出すとき,実引数の配列の前に * を与えて, 1個の配列を別々の仮引数に分けて渡すことができる。
def baz(a, b, c, *rest) p a p b p c p rest end x = %w(Hot Tot Jin Jod Fie Fly Zan Zod Pik Snik Lun Lod Ichabod) baz(*x)
を実行すると,このようになる。
$ jruby star.rb "Hot" "Tot" "Jin" ["Jod", "Fie", "Fly", "Zan", "Zod", "Pik", "Snik", "Lun", "Lod", "Ichabod"] $
baz(*x) のかわりに baz(13, :Thaegan, :children, *x)
を呼び出したらどうなるでしょうか。
それはメソッド定義とメソッド呼出しの両方から * を消した場合と比べ,どのように同じで,
どのように違うでしょうか。
(ヒント: object_id)
実引数としてハッシュを渡すとき,あいまいさがなければ波括弧を省略してよい。 例えば,
def baz(a, b) end
に対して,こう呼び出してよい。 b は {:a=>10, :b=>20} というハッシュになる。
baz "test", :a=>10, :b=>20
5.1.1 多重代入
メソッド呼出しで実引数から仮引数へ値が渡されるとき, 変数への代入と同じく,オブジェクトへの参照 (つまりポインタ) が渡される (どちらの場合も,内部実装としては,nil など特定のオブジェクトは 参照ではなくオブジェクトそのものが渡されるかもしれない。 しかし,そういった事情は Ruby プログラムからは安全に無視できる)。 引数渡しと代入演算は実質同じである。
この類似性から類推できるとおり, メソッド呼出しで複数の実引数値を複数の仮引数に渡せるように, 代入演算で複数の値を複数の変数に渡すことができる。
a, b = 1, 2 # a = 1; b = 2
右辺の式の並びは配列オブジェクトで代用できる。
a, b = [1, 2] # a = 1; b = 2
メソッド呼出しの仮引数と実引数で * を指定できるのと同じく, 代入の左辺と右辺でも * を指定できる。
a, *b = [1, 2, 3, 4] # a = 1; b = [2, 3, 4] a, b, c = 1, *[2, 3] # a = 1; b = 2; c = 3
ただし,メソッド呼出しでは引数の個数 (いわゆる arity) がマッチしないと引数エラー ArgumentError が発生するが,代入演算はそのように厳しくない。 左辺が多すぎるとき,あぶれた変数には nil が代入される。 右辺が多すぎるとき,あぶれた値は単に捨てられる。
このような多重代入の典型的な用法の一つは, メソッドからの複数の戻り値の受け取りである。
def f return 1, 2 # 配列 [1, 2] を構築して返す end x, y = f() # x = 1; y = 2
5.2 クラスのためのメソッド
これまでの例はどれも,クラスのインスタンスから呼び出せるインスタンス・メソッドを定義してきた。 では,(new メソッドのように) クラス自身から呼び出せるメソッドはどうしたら定義できるだろうか?
この問題に対し,クラスに基づくオブジェクト指向言語としての正攻法は,メタクラスに対するメソッドとして定義する方法である。 メタクラスのインスタンス・メソッドは,クラスにとってのクラス・メソッドである。 Ruby のメタクラスは開いているから,いつでもメソッドを追加定義できる。
class Class def baz puts "this is baz" end end Array.baz String.baz Object.baz
すぐに分かるように,これは Ruby の すべてのクラス に対して等しく baz というクラス・メソッドを定義する。 いわば new メソッドと同格のメソッドを追加しているわけである。 これは Ruby という言語そのものを別の姿にしかねない強力な方法だが, おそらく君が日常的に望むものとは違っているだろう。
全クラスに対してではなく, C++ の静的メンバ関数や Java の静的メソッドのように あるクラスに対してだけ,クラス自身から呼び出せるメソッドを定義するには, どうすればよいだろうか?
Ruby には 特異メソッド (singleton method) という, ある特定の1個のインスタンスに専属するメソッド を定義する機構が用意されている。 これをメタクラスのインスタンスであるクラスに応用すればよい。 一般のインスタンスに対する特異メソッドについては次々節で述べる。
クラス Bar に対し,
def Bar.baz end
class Bar def self.baz end end
class <<Bar def baz end end
この三つはどれも Bar に専属する特異メソッド baz を定義する。
たとえトップレベルにあっても def Bar.baz は
グローバル関数ではなく Bar に専属する特異メソッドを定義する。
特異メソッドは
Bar.baz
または
Bar::baz
という構文で呼び出すことができる。 この呼出しで Bar はインスタンス扱いであり,レシーバの値は Bar である。
5.3 mix-in の方法: include と extend
下記はモジュール Foo にインスタンス・メソッド baz を定義する。
module Foo def baz end end
Foo はモジュールだから,自分ではインスタンスを作ることができず,そのメソッドを呼び出せない。 しかし,他の任意のクラスは,モジュール Foo を include することにより, そのメソッド baz を呼び出すことができる。 include はメタクラス Module の private インスタンス・メソッドである (つまり,事実上,モジュールやクラスを定義する時,その定義本体内だけで呼び出せる関数である)。 慣例として include の呼出しでは丸括弧は省く。
class ARealClass include Foo end ARealClass.new.baz
このとき,モジュール Foo のすべてのインスタンス・メソッドが, ARealClass の任意のインスタンスをレシーバとして呼び出せる。 Foo にメソッドを追加したときは,そのメソッドも ARealClass の任意のインスタンスをレシーバとして呼び出せる。 メソッドだけでなく,定数も取り込まれる。 もしも Foo に K という定数が定義されていたら, 同じ定数に ARealClass::K でアクセスできる。 ARealClass でメソッドを定義したとき, そのメソッドの中からは単に K でアクセスできる。
このようなモジュールの取込みは mix-in と呼ばれ,Ruby ライブラリの多くの機能がこの方法で作成されている。 例えば,Array の興味深いメソッドの多くがモジュール Enumerable に由来している (第6章 参照)。
1個のインスタンスに対してだけモジュールを mix-in したいときは, Object のインスタンス・メソッド extend を使えばよい。
a = Object.new a.extend Foo a.baz
これはインスタンスに対して,モジュールのインスタンス・メソッドを特異メソッドとして追加する。
5.4 特異メソッドと特異クラス
5.2 節で述べたように,特異メソッドとは,ある特定の1個のインスタンスに専属するメソッドである。 5.2 節ではメタクラスのインスタンスであるクラスに対する特異メソッドを論じた。 5.3 節の extend は一般のインスタンスに特異メソッドをまとめて定義する高水準の方法である。
任意のインスタンス x に対し,特異メソッド f を定義する基礎的な方法は,
def x.f end
または
class <<x def f end end
である。 5.2 節で述べた方法は,x がたまたまクラス (=メタクラスのインスタンス) だった場合に該当する。
しかし,ここで君は矛盾を感じているはずだ。 5.1 節によれば Ruby では「定数と同じく,メソッドはモジュールかクラスに属する」はずだ。 そうだとすれば,メソッドが「ある特定の1個のインスタンスに専属する」ことは出来ないはずだ。
実際には,Ruby は特異メソッドを定義するとき,その裏側でひそかに 特異クラス (singleton class) と呼ばれるクラスを自動生成することによって, メソッドがモジュールかクラスに属するという一貫性を維持している。 特異クラスとは,ある特定の1個のインスタンスに固有の無名クラスである。 究極的には,Ruby におけるあらゆるインスタンスがそれ自身の特異クラスを持ち得る。
次の特異メソッド定義を例にして考えよう。
a = Object.new def a.foo1 puts "Hi" end a.foo1
このとき,特異メソッド foo1 は,実際にはインスタンス a に直接,専属するのではなく, インスタンス a のためだけに自動生成される特異クラス上に定義される。 つまり,特異メソッドとは,実は,特異クラスに属するメソッド である。
Ruby は特異クラスを,影の存在として,できるかぎり表に出さないように努めている。 メソッド呼出し a.class はあくまで表向きのクラスを返す。 影の存在である特異クラスへは普通はアクセスできない。 特異クラスに属するメソッドは,特異クラスが表に出ない限りは, 特異クラスと1対1に対応する特定の1個のインスタンスに専属すると見なせる。 これが特異メソッドの正体である。
次のコードは,オブジェクトの特異クラスへのアクセスを可能にする。 これは,特異メソッドを定義するための class…end 構文の中で self が特異クラスであることを利用し,その値を定義本体の最後の値として取得する。
class Object def singleton_class class <<self self end end end
メソッド singleton_class を Ruby の任意のオブジェクトに対して呼び出せば, その特異クラスを得ることができる。 ただし,欠点として,もともと特異クラスをもたないオブジェクトでも, このメソッドにかければ,特異クラスが自動生成される。
5.5 定数と関数のための名前空間としてのモジュール
モジュールは名前空間として利用できる。 クラス名やモジュール名その他一般の定数について, モジュール内に定義を入れ子にし,2重コロン記法でアクセスできること, 繁雑さを避けるため,他の定数や変数で自由にショートカットを設けられることを, 君は第3章で学んだ。
しかし,まだ一つ大きな欠落がある。 それは関数定義だ。 もしも君がトップレベルのグローバルな定数定義と関数定義
K = 1 def baz end
を,名前空間の分割のため,モジュールの中に入れ子にしたいと思ったとき,
module Foo K = 1 def baz end end
と,単純に module でくくっただけではうまく行かない。 インスタンス・メソッドを定義することになるからである。 モジュールは自分ではインスタンスを作れないから,どこかのクラスに mix-in してもらって, そのインスタンスで baz メソッドを呼び出すしかない。
しかし,特異メソッドを使えば,この問題は解決される。
module Foo K = 1 def self.baz end end
定数 K を Foo::K としてアクセスできるのと同じく, baz メソッドを Foo::baz または Foo.baz として呼び出すことができる。
だが,これで十分だろうか?
モジュールをクラスに mix-in したときは,最初の例のようにインスタンス・メソッドとして単に baz と呼び出せるようにできないだろうか。 話を蒸し返しているようだが,これは決して大それた望みではない。 定数ならば,mix-in したとき,単に K と参照できるのだから,対称性を考えれば当然の要望である。
まさにこのことを行うメソッド module_function がメタクラス Module の private インスタンス・メソッドとして用意されている。
module Foo K = 1 def baz end module_function :baz end
module_function は,実引数としてシンボルが与えられたとき, モジュールに対するその名前のインスタンス・メソッドをコピーして, 同じ名前でモジュールの特異メソッドを新しく定義するとともに, もとのインスタンス・メソッドを private にする (つまり,mix-in したクラスのインスタンスからレシーバを省略してのみ呼び出せるようにする)。
module_function を無引数で呼び出すと, それ以降,そのモジュールで新しく定義されるメソッドすべてに,このような処理が行われる。
module Foo module_function K = 1 def baz end end
では,これで名前空間としてモジュールを使う方法は万全だろうか?
もしも君がここで何か実用的な規模のモジュールづくりに取り掛かったとしたら,おそらく困った事態になるだろう。
module APracticalModule module_function def baz end class Bar def initialize baz # ここで NameError: undefined local variable or method `baz' for … end end end bar = APracticalModule::Bar.new
入れ子のクラスのメソッドから,(典型的には共通ユーティリティである) 関数 baz を呼び出そうとしてもアクセスできない。 このことから分かるように, トップレベルでのグローバルな関数とクラスなどの定数の定義群を,単純に module でくくり, module_function を呼び出しただけでは,名前空間としてのモジュールへの閉じ込めはまだ不完全である。
一つの解決方法は,5.3 節で述べた include メソッドを使って, モジュールを入れ子のクラスに mix-in する方法である。
module APracticalModule module_function def baz end class Bar include APracticalModule # 注目! def initialize baz end end end bar = APracticalModule::Bar.new
この例では入れ子のクラス定義が1個だけだから目立たないが,長いモジュール名が何度も繰り返されると, 君はうんざりするかもしれない。Emacs の動的略称展開などエディタによる入力補助があったとしても, あとでリストを読むときに不快感をおぼえるかもしれない。 だが,実用上,名前の衝突を避けるため,外部名となるモジュール名はあまり短くできない。
しかし,モジュール内に限れば,その中で矛盾しない限り,短い良い名前を自由に使える。 そもそも,そのための名前空間のはずだ。したがって,例えば,
module APracticalModule module_function AP = APracticalModule # 注目! def baz end class Bar include AP # 注目! def initialize baz end end end bar = APracticalModule::Bar.new
とできる。 もう一つの方法は,より素朴に AP::baz としてメソッドを参照する方法である。 モジュール名を短い名前にしているから,これも十分実用的である。
module APracticalModule module_function AP = APracticalModule # 注目! def baz end class Bar def initialize AP::baz # 注目! end end end bar = APracticalModule::Bar.new
この方法の実例は Ruby による Lisp インタープリタ L2Lisp.rb に見ることができる。