【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の動的定義をしていきたいと思います。
今回は以上です。
プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)
- 作者:伊藤 淳一
- 発売日: 2017/11/25
- メディア: 大型本