みずラテ

牛乳と水を2対1で。

【Ruby】なぜ動的にclassを生成する際にObject.const_setとなるのか

ふとした疑問のメモです。

やっていたこと

rubyのメタプログラミングで動的にクラスの生成を行いたくて調べていたところ、Object.const_setで生成出来ることがわかった。

疑問

まず以下を試してみてください。

Object.respond_to?(:const_set)
#-> true

Objectクラスはconst_setメソッドを持っていることがわかります。
一方で、以下のようにObjectクラスの親クラスであるBasicObjectクラスに同じメソッドがあるか見てみると、

Object.superclass
#-> BasicObject
BasicObject.respond_to?(:const_set)
#-> true

ありました。ということは、Objectクラスのconst_setメソッドはBasicObjectのメソッドを継承したものだということが分かります。

ちなみに

Class.superclass
#-> Module
Module.superclass
#-> Object
Class.respond_to?(:const_set)
#-> true

ClassクラスもModuleクラスもconst_setメソッドを持っています。

じゃあ、Object.const_setじゃなくて、BasicObject.const_setでも、Module.const_setでも、Class.const_setでもいいのでは?

というのが今回の疑問

実際、

Class.const_set :Foo, Class.new
#-> Class::Foo
BasicObject.const_set :Daa, Class.new
#-> BasicObject::Daa

Classクラスを継承したFooクラスを生成出来ます。

普通のクラスは何クラスを継承している?

じゃあ普通のクラスは何クラスを継承しているのでしょう。BasicObjectクラス?Objectクラス?Moduleクラス?Classクラス?
正解は

class Foo
end
f = Foo.new
f.class
#-> Foo
f.class.superclass
#-> Object

結果

ということで普通に定義して生成したFooクラスのオブジェクトはObjectクラスを継承していました。
今回はconst_setメソッドで動的に普通のクラスを定義してインスタンスを生成したいので、普通の静的なクラス定義生成したオブジェクトと同じ様に、そのインスタンスはObjectクラスを親クラスに持つ様に作ったほうが良さそうです。

なので、Object.const_setを使うという結論になりました。
じゃあ、なぜ普通のクラスのインスタンスはObjectクラスを継承しているの?という疑問出てきますが、それはリファレンスマニュアルを見てみると、
class Class (Ruby 2.7.0 リファレンスマニュアル)

new(superclass = Object) -> Class

newメソッドの引数にはsuperclassを指定できて、デフォルトがObjectクラスだからなんですね。

ようやく腹落ちしました。

結論

おとなしくObject.const_setメソッドを使ってclassの動的定義をしていきたいと思います。

今回は以上です。

djandjan.hateblo.jp