概要
同名のメソッドを持つ複数の Module を include した場合、メソッドを呼び出したときにどういう動きをするんだろう?という疑問が唐突に頭を過ぎりました。本当に唐突ですみません。
最初は事例やドキュメントを探してみようかと思ったのですが、これなら実際やってみたほうが早いと思いましたので簡単なサンプルコード書いて確認してみました。結果を以下に記載していきたいと思います。
予想
- include でコケる
- メソッドの呼び出しでコケる
- 最後に include された Module に実装されたメソッドの動きになる
3っぽい気はします。
環境
ruby 2.5.1
結論
選択肢3 が正解でした。後勝ち みたいですね。 include の順番を入れ替えたら出力結果が変わることも確認できました。
検証用サンプルコード
適当な Module を3つ用意し、全てのモジュールに run
メソッドを実装しました。これを全て include して run
を実行したときにどのような動きをするかを確認する、というかんじで使いました。
そもそも Module の設計とかメソッドの名前とかに問題があるんじゃね?と言われたらぐうの音も出ないのでひとまずここではスルーしてくださいw
# module
module ModA
def run
p :ModA
end
end
module ModB
def run
p :ModB
end
end
module ModC
def run
p :ModC
end
end
# class
class Sample
include ModA
include ModB
include ModC
end
# main
Sample.new.run # => "ModC"
追記
@Nabetani さんから、さらに充実した検証コードを下記コメントでご教示いただきました。感謝。
https://qiita.com/tommy_aka_jps/items/f02732d993a726574f83#comment-761cc0f00cb95cc869b1
※環境によってはアンカーリンクがうまく機能していないようなので、前述のコメントからコードを転載します。
module ModA
def run; p :ModA ;end
end
module ModB
def run; p :ModB ;end
end
module ModC
def run; p :ModC ;end
end
module ModD
def run; p :ModD ;end
end
class UseInclude
include ModA
def run; p :UseInclude; end
end
UseInclude.new.run #=> :UseInclude
class UsePrepend
prepend ModA
def run; p :UsePrepend; end
end
UsePrepend.new.run #=> :ModA
o = UsePrepend.new
def o.run; p :"o.run"; end
o.run #=> :"o.run"
o.class.prepend ModB
o.run #=> :"o.run"
o.singleton_class.include ModC
o.run #=> :"o.run"
o.singleton_class.prepend ModD
o.run #=> :ModD
おわりに
よく考えてみればそんなに意外な結果ではないですかね。仮に Sample
クラスで run
メソッドをオーバーライドしたら当然そっちで動きますしね。 Module って C# で言うところの interface みたいなイメージを持っていたので、メソッド呼び出しの際に型指定がないとエラーになるかと勘違いしていました。クラスの多重継承を扱える言語はまだ触ったことがないので他の言語だとどうなんでしょうね。C++ とか詳しい方、よろしければご教示ください。(他力本願)