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を使おう(翻訳)

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ