はじめに
ruby gold取得に向けて勉強中superについて自分の中での理解が変わり共有したいと思いました。
本題
まず初めに以下のコードを実行するとどのような結果が出力されるでしょう?
module M
def foo
super
puts "M#foo"
end
end
class C2
def foo
puts "C2#foo"
end
end
class C < C2
def foo
super
puts "C#foo"
end
include M
end
C.new.foo
選択肢1
C2#foo
C#foo
選択肢2
C2#foo
M#foo
C#foo
選択肢3
エラーが表示される
僕はずっと1と思ってました。
ちなみに正解は 選択肢2
きちんと理解して正解した方はもうこれ以上見なくて大丈夫です。
ここまで見てくださってありがとうございます。
まず、僕がどのようにして選択肢1を選んだのか
「Cクラスのfooメソッドやな、ふむふむ」
「お、superか。てことは親クラスのfooを見ればいいんやな」
「なるほど、答えは1や!」
こんな感じです。
モジュールをincludeしたところで呼ばれないと思っていました。
superクラスについて学んでいきましょう。
まず初めにリファレンスマニュアルに書いてあるsuperについて簡単に
super は現在のメソッドがオーバーライドしているメソッドを呼び出します
なるほど、スーパークラスのメソッドを呼び出すわけではなさそうです。
ではancestorsメソッドを使用してみます。
## 省略
class C < C2
def foo
super
puts "C#foo"
end
include M
self.ancestors #=> [C, M, C2, Object, Kernel, BasicObject]
end
C.new.foo
ancestorsは簡単に説明するとメソッドの探索順位がわかるメソッドです。
例えばfooメソッドを実行した時Cのfooメソッドを探し、なければMのfoo...と探索していきます。
ここでもう一度問題のコードを見ていきます。
module M
def foo
super # C2のfooを参照する
puts "M#foo"
end
end
class C2
def foo
puts "C2#foo"
end
end
class C < C2
def foo
super # Mのfooを参照する
puts "C#foo"
end
include M
self.ancestors #=> [C, M, C2, Object, Kernel, BasicObject]
end
C.new.foo
#=> C2#foo
#=> M#foo
#=> C#foo
結論
- superの記述があると次のメソッドの探索が行われる
- 親クラスのメソッドを探索するわけではない
おわりに
資格勉強していると結構理解が深まるので実務経験が浅い方は勉強してみてもいいのではないでしょうか?