サカナ未遂

プログラミング、筋トレ、子育て

Rubyの継承チェーンについてのメモ

Rubyの継承チェーンについて勉強したのでメモです。

継承チェーンとは?

Rubyのクラスのスーパークラスを見る、そのスーパークラススーパークラスを見る、をBasicObjectが見つかるまで続ける。
このクラスの道筋が継承チェーンとなります。
Stringクラスの場合だと以下のようになります。

irb(main):001:0> String.superclass
=> Object
irb(main):002:0> Object.superclass
=> BasicObject
irb(main):003:0>


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」があります。
これらは、クラスではなくモジュールとなります。

モジュールはクラスにインクルードされることで、クラスの継承チェーンに挿入されます。
それではインクルードした場合、継承チェーンのどこに挿入されるのでしょうか?
またインクルードする順番は関係するのでしょうか?
試してみましょう。

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版

メタプログラミングRuby 第2版