Tech Racho エンジニアの「?」を「!」に。
  • 開発

Ruby: `super`キーワードの4つの側面(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。


同サイトより

日本語タイトルは内容に即しました

Ruby: superキーワードの4つの側面(翻訳)

私の発行するRuby 💌ニュースレター💌はこちらです!ご自由にサブスクライブいただけます!🚀

本記事では以下のトピックについて扱います。

  • 暗黙の引数
  • super vs super()
  • ブロック付きsuper
  • ancestorsチェイン付きsuper

暗黙の引数

引数を使うメソッドが、子クラスのどれかにあるメソッドによってオーバーライドされている状態で、その子クラス内でsuperを引数なしで呼び出すと、子クラスのメソッドの引数が自動的に親クラスのメソッドに渡されます。

次の例をちょっとご覧ください。

class Parent
  def say(message)
    puts message
  end
end

class Child < Parent
  def say(message)
    super
  end
end

irb> Child.new.say('Hi!')
#=> Hi!

ChildクラスはParentクラスを継承し、このChildクラスはParent#sayメソッドをオーバーライドしています。

Child#sayメソッド内では、superに引数を渡さずに呼び出しています。

すなわち、Rubyは#sayメソッドの探索をChildクラスの「ancestorsチェイン」内で行い、見つかったメソッドにmessage引数を渡します。

メモ: Rubyのancestorsチェインのメカニズムについて知りたい方は私の別記事もどうぞ。

しかし、ここでParent#sayメソッドが引数をまったく取らないとしたらどうでしょうか。

super vs super()

先ほどのParent#sayメソッドを再定義して、message引数を取っ払ってみましょう。

class Parent
  def say
    puts "親です"
  end
end

class Child < Parent
  def say(message)
    super
  end
end

irb> Child.new.say('Hi!')
#=> ArgumentError (wrong number of arguments (given 1, expected 0))

Parent#sayメソッドが引数を受け取らないにもかかわらず、Child#saysuperを呼んだためにChild#sayメソッドのmessage引数が暗黙でParent#sayメソッドに渡されてしまいました。

この問題を回避するには、Child#sayメソッドに渡される引数を受け取らないようsuperに明示的に指示を出す必要があります。

そのためには、superキーワードに丸かっこを追加して、super()とします。

class Parent
  def say
    puts "親です"
  end
end

class Child < Parent
  def say(message)
    super()
  end
end

irb> Child.new.say('Hi!')
#=> 親です

お次は、Parent#sayメソッドにブロックを1つ渡してみましょう。

ブロック付きsuper

Parent#sayメソッドを再定義して、yieldキーワードを追加しましょう。

class Parent
  def say
    yield
  end
end

class Child < Parent
  def say
    super
  end
end

irb> Child.new.say { puts 'よかった!ママかパパだ' }
#=> よかった!ママかパパだ

Child.new.sayメソッド呼び出しに渡されたブロックは、superキーワードを経由して、Parent#sayメソッドに暗黙で渡されます。

お次はyieldキーワードでこのブロックをキャッチし、Parent#sayメソッド内で実行してみましょう。

メモ: yieldについて知りたい方は私の別記事もどうぞ。

ancestorsチェイン付きsuper

#sayメソッドが定義されているGrandParentクラスをParentクラスに継承しましょう。

class GrandParent
  def say(message)
    puts "おじいちゃんかおばあちゃん: #{message}"
  end
end

class Parent < GrandParent
end

class Child < Parent
  def say(message)
    super
  end
end

irb> Child.new.say('Hi!')
#=> おじいちゃんかおばあちゃん: Hi!

ここでのsuperキーワードは、#sayメソッドの探索をParentクラス内で試みます。

Parentクラスにはこのメソッドが定義されていないため、superは続いてParentクラスのスーパークラス内(つまりGrandParentクラス)でメソッド探索を試みます。

GrandParentクラスには#sayメソッドが定義されています。

これで、Child.new.sayメソッド呼び出しに渡された'Hi!'引数は、superキーワードを経由してGrandParent#sayメソッドに暗黙で渡されます。

いかがでしたか?


本記事をお読みいただきありがとうございました😊。

本記事がお役に立ちましたら、ぜひMedium.comの元記事で存分に👏ボタンを押してください。

前回の私の記事『Method Arguments in Ruby: Part II – Mehdi Farsi – Medium』もどうぞ。

関連記事

Ruby: ループには一時変数ではなくEnumerableを使おう(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。