Rubyで普段から使ってるStringクラスとか、どういったクラスを継承しているのか気になったため、調べたことをまとめておきます。
継承チェーンとは?
Rubyのクラスのスーパークラスを見る、そのスーパークラスのスーパークラスを見る、をBasicObjectが見つかるまで続ける。
このクラスの道筋が継承チェーンとなります。
Stringクラスの場合だと以下のようになります。
irbで確認してみましょう
# Stringのスーパークラスを確認
irb(main):001:0> String.superclass
=> Object
# StringのスーパークラスであるObjectのスーパークラスを確認
irb(main):002:0> Object.superclass
=> BasicObject
StringクラスのスーパークラスがObjectクラス、ObjectクラスのスーパークラスがBasicObjectクラスとなっています。
ちなみにBasicObjectクラスを見ると
irb(main):003:0> BasicObject.superclass
=> nil
となっているので、BasicObjectクラスが終点であることがわかります。
継承チェーンのモジュールについて
しかしここでクラスの親クラスを返すancestorsメソッドを使用して見ると
irb(main):004:0> String.ancestors
=> [String, Comparable, Object, Kernel, BasicObject]
先ほどスーパークラスで辿った時に出てこなかった「Comparable」と「Kernel」が出てきましたね。
これらは、クラスではなくモジュールとなります。
モジュールはクラスにインクルードされることで、クラスの継承チェーンに挿入されます。
KernelはObjectクラスにインクルードされています。
https://docs.ruby-lang.org/ja/latest/class/Kernel.html
ComparableはStringにインクルードされています。
https://docs.ruby-lang.org/ja/latest/class/String.html
このようにString一つとっても複数のクラスとモジュールが継承されていることがわかります。
次にインクルードをする場合、継承チェーンのどこに挿入されるのでしょうか?
またインクルードする順番は関係するのでしょうか?
試してみましょう。
以下のコードを動かしてみます。
module A
end
module B
end
class C
include A
include B
end
p C.ancestors
実行結果は
[C, B, A, Object, Kernel, BasicObject]
まず自作クラスのスーパークラスはデフォルトでObjectクラスになるのですが、
インクルードされたModuleがスーパークラスの前に差し込まれています。
またBの方がAより先に来ています、
つまりインクルードすると、
・自分のクラスの後に差し込まれる
・複数インクルードすると後に指定した方が継承チェーンでは先に来る
となります。
ちなみに
module A
end
module B
end
class C
prepend A #includeをprependに変更
include B
end
p C.ancestors
とincludeをprependに変更すると、自分のクラスの後ではなく先に挿入されます。
以下実行結果です
[A, C, B, Object, Kernel, BasicObject]
Cより先にAが来ていますね。
このように継承チェーンのどこに差し込むかは、includeを使うかprependを使うか、
includeの場合は、上に書くか下に書くかで変わって来ます。
参考文献
メタプログラミングRuby 第2版