読者です 読者をやめる 読者になる 読者になる

特異クラス定義構文を使って機能拡張したオブジェクトのクラスはどうなるのか?

Ruby

答え : 拡張前と同じ。

以下、脳内理解プロセスをだらだら記述。Rubyよくわかってる人は読み飛ばすが吉。

それを確かめる簡単なコードを書いた。

  1. array : Arrayクラスのインスタンス
  2. array_ext : Arrayクラスのインスタンスに特異メソッドm1を追加したオブジェクト
array = []
array_ext = []

def << array_ext
  def m1
  end
end

array2 = []


p array.class
p array.instance_of?(Array)
p array_ext.class
p array_ext.instance_of?(Array)
p array2.class
p array2.instance_of?(Array)


# => Array
# => True
# => Array
# => True
# => Array
# => True

感覚的には、この結果は奇妙だ。arrayとarray_extは同じクラスのインスタンスだと言っている。

特異クラス定義式はオブジェクト(=インスタンス)に対してメソッド等を定義する式だ。class式による通常のクラス定義式と異なり、クラスにメソッド等を定義するわけではない。上記コードで言えば、クラスであるArrayは全く変更されない。だからarrayもarray_extもクラスは同じArrayのままだ。それはわかっている。でも胸の奥ががもやもやするのはなぜだろう。

クラスとインスタンスの初心者向け解説でよく見るたとえ話「鯛焼きのカタと鯛焼き」に照らし合わせて考えてみると、この奇妙さの正体がわかってきた。

「鯛焼きのカタと鯛焼き」の話では、鯛焼きはインスタンスで、鯛焼きのカタはクラスを示している。鯛焼き(インスタンス)は、鯛焼きのカタ(クラス)から生成される。尾びれが一つしかない鯛焼きの型から作られる鯛焼きの尾びれの数は、やっぱり一つだ。もし尾びれが二つある鯛焼きがあったのならば、この鯛焼きは尾びれが一つの鯛焼きとは異なるカタから作られたものといえるだろう。尾びれが一つのカタからは尾びれが一つの鯛焼きしか焼けないのだから。

しかし、前述したコードはそれを否定している。かたや尾びれが一つの鯛焼き(array)で、かたや「m1メソッド」というもう一つの尾びれを持つ鯛焼き(array_ext)。両者は明らかに形が異なる鯛焼きであるのに、これらの鯛焼きを焼いたカタは全く同じものだという。

ここで、何が自分の胸をつかえさせているのかがわかった。自分の頭では「クラス=鯛焼きのカタ」という図式が成り立っている。だから「特異クラス定義」と言うのなら「特異な鯛焼きのカタ」を作っているのだろう、と思いこんでいたことだ。実際、「特異クラス定義」で行われているのは「焼き上がった鯛焼き」の加工だ。「鯛焼きのカタ」の加工ではない。この特異クラス定義という用語中の「クラス」の使われ方が、自分にとっては非常に紛らわしかったのだ。

いっそ特異「クラス」定義なんて呼び方をしないで、特異「インスタンス」定義とか特異「オブジェクト」定義とか言ってくれればイメージしやすかったのにな、と思う*1。まあこれはこれでいろいろ誤解を生みそうな呼び方だけど。あるいはクラスとインスタンス、みたいな用語は取っ払って、Javascriptみたいに全部オブジェクトです、って割り切っちゃえばわかりやすいのかも。ああ、そういえばRubyも全部オブジェクトです、って言ってた言語だった。全部オブジェクトって、そういうつもりで言っていたのか。クラスとかインスタンスとか、そういう用語はJavaC++に慣れた人たちが入りやすくするためのシンタックスシュガー的なものとかそんな感じなのかしら。

とりあえず魚の骨はとれたようなのでよしとする。というわけで脳内の自己解決プロセスをだらだらと日記に書いてみるテストでした。Rubyとかよく理解しないでいたから、知っている人には何を今更、的な内容でしたね。

*1:ちなみに特異メソッド定義を用いてクラスメソッドを定義すると、「もう一つの尾びれを持つ鯛焼きのカタ」を作ることができる。特異「メソッド」定義というインスタンスを想起させる用語が用いられている手法が「クラス」を加工するというのがなんとも。特異クラス定義と特異メソッド定義の名前逆にしちゃえばいいのに。まあ、特異メソッド定義を使って作った「もう一つの尾びれを持つ鯛焼きのカタ」からできる鯛焼きの尾びれはやっぱり一つだったりするから、「もう一つの尾びれを持つ鯛焼きのカタ」が作れるとはいえないけど