その他

    super の話

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    こんにちは、bibioです。ruby には super というオーバーライドしているメソッドを呼び出す機能があります。

    何気なく使うとはまることがあるので違いを区別します。

    説明した通り、superはオーバーライドしているメソッドを呼び出す機能です。

    class A
      def foo
        puts "A#foo"
      end
    end
    
    class B < A
      def foo
        super
        puts "B#bar"
      end
    end
    

    のとき、次のように出力されます。

    B.new.foo 
    =>
    A#foo
    B#bar
    

    では、以下のような例だとどうでしょうか?

    class A
      def foo
        puts "A#foo"
      end
    end
    
    module M
      def foo
         puts "M#foo"
      end
    end
    
    class B < A
      include M
      def foo
        super
        puts "B#bar"
      end
    end
    

    突然ですが、問題です。 B.new.foo() を実行したときに出力されるのは次のうちどれでしょうか?

    (1)

    M#bar

    A#bar

    B#bar

    (2)

    A#bar

    B#bar

    (3)

    M#bar

    B#bar

    正解は・・・(3)

    super はオーバーライドをするメソッドを呼び出すのであって、常に親クラスのメソッドを呼び出すわけではありません。名前に引きずられそうですね。

    何もMix-inされていない場合、オーバーライドする親クラスのメソッドが呼び出されます。(なければ、その親クラスの親)といって、どれにもマッチしなければ method_missing が呼ばれます。通常は、NoMethodError になります。

    オーバーライドされたメソッド呼び出しの階層を注意します。

    include でモジュールをMix-inした場合は、元のクラスの上位に割り込みます。

    メソッドの探索順にいえば、A->M->B になります。

    メソッドの階層は ancestorsメソッドを使って調べます。上の例だと次の通り(Ruby2.1.xの場合)

    irb> B.ancestors
    => [B, M, A, Object, Kernel, BasicObject]