Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
Ruby TIPS

Ruby TIPS

クラスのメソッドをオーバーライドするには?

2016年12月16日

継承先クラスの新メソッドで元クラスの既存メソッドをオーバーライドして異なる機能に置き換える方法と、新メソッド内から既存メソッドを呼び出すことで既存機能に新機能を追加する方法を説明する。

ローグ・インターナショナル 羽山 博
  • このエントリーをはてなブックマークに追加

 サブクラスでメソッドをオーバーライドすれば、スーパークラスの機能と同じ名前で、異なる機能を持つメソッドを作成したり、スーパークラスのメソッドに機能を追加したメソッドを作成したりできる。今回はメソッドをオーバーライドする方法を見る。

メソッドをオーバーライドする

 オーバーライドとは、スーパークラスのメソッドを「再定義」することと考えればよい。従って、オーバーライドされたメソッドには同じ名前が使われる。では、サブクラスの作成とメソッドのオーバーライドを具体的な例で見ていこう。

 例えば、猫を表すCatクラスがあり、そのクラスを継承したTigerクラスを作成したものとする。Tigerクラスはもちろん「虎」を表すクラスである。虎はネコ科なので猫の性質や働きを受け継いでいるというわけである。

 ここからは順を追って見ていくので、オーバーライドは少しだけ後回しにして、まずは、単にクラスを継承しただけのコードを書いてみよう。

sample001.rb
class Cat
  def meow
    return "にゃあ"
  end
end

class Tiger < Cat

end

animal = Tiger.new
puts animal.meow
リスト1.1 Catクラスを継承したTigerクラスを作る

ここでは、コードを簡単にするために余計な機能は全て省いてある。Catクラスで定義されているmeowメソッドは猫の鳴き声の「にゃあ」という文字列を返す。一方、CatクラスのサブクラスであるTigerクラスには、何もメソッドを定義していない。最後に、Tigerクラスのインスタンスを作成し、meowメソッドを呼び出している。

 Tigerクラスは単にCatクラスを継承しただけで、新しい機能は全く追加していない。つまり、Tigerクラスには、meowメソッドは書かれていない。この場合、Tigerクラスのmeowメソッドを呼び出すと、スーパークラスで定義されているmeowメソッドが呼び出される。実行例は以下の通り。

コンソール
$ ruby sample001.rb 
にゃあ
$ 
実行例1.1 クラスの継承を利用したプログラムを実行する

Tigerクラスのmeowメソッドを呼び出したが、Tigerクラスにはmeowメソッドが定義されていないので、スーパークラスのmeowメソッドが呼び出され、「にゃあ」と表示された。

 サブクラスで定義していないメソッドを呼び出したとき、スーパークラスに同じ名前のメソッドがあれば、スーパークラスのメソッドが呼び出される。ところが、サブクラスでちょっと違った機能を提供したり、機能を追加したりしたいことがある。例えば、同じ鳴き声といっても、猫は「にゃあ」と鳴き、虎は「がおー」と吠える。そこで、Tigerクラスではmeowメソッドが「がおー」という文字列を返すようにしよう。

 ここで、オーバーライドを利用する。これにはリスト1.2のように、単にサブクラスで同じ名前のメソッドを定義すればよい。

sample002.rb
class Cat
  def meow
    return "にゃあ"
  end
end

class Tiger < Cat
  def meow
    return "がおー"
  end
end

animal = Tiger.new
puts animal.meow
リスト1.2 Tigerクラスでmeowメソッドをオーバーライドする

Catクラスのmeowメソッドは「にゃあ」という文字列を返し、そのサブクラスであるTigerクラスのmeowメソッドは「がおー」という文字列を返す。

 実行例は以下の通り。

コンソール
$ ruby sample002.rb 
がおー
$ 
実行例1.2 オーバーライドしたメソッドを利用する

単に「がおー」と表示されるだけであるが、Tigerクラスのmeowメソッドが呼び出されていることが分かる。

 ところで、場合によっては虎も「猫なで声」を出すこともあるかもしれない。そこで、必要に応じてスーパークラスのmeowメソッドも呼び出せるようにしよう。

 スーパークラスのメソッドはsuperと書くだけで呼び出せる。その場合、スーパークラスのメソッドには、同じ引数が渡されることに注意。スーパークラスのメソッドを引数なしで呼び出したいときには、super()のように()を付ける必要がある。

sample003.rb
class Cat
  def meow
    return "にゃあ"
  end
end

class Tiger < Cat
  def meow(flg) 
    if flg 
      return "がおー"
    else
      return super()
    end
  end
end

animal = Tiger.new
puts animal.meow(true)
puts animal.meow(false)
リスト1.3 スーパークラスのメソッドを呼び出す

meowメソッドの仮引数flgに渡される値がtrueであれば「がおー」という文字列が返され、falseであれば、スーパークラスのmeowメソッドが呼び出される。super()を付けているので、引数なしでスーパークラスのmeowメソッドが呼び出させる。

 実行例は以下の通り。最初に「がおー」と表示され、次に「にゃあ」と表示される。

コンソール
$ ruby sample003.rb 
がおー
にゃあ
$ 
実行例1.3 スーパークラスのメソッドを呼び出す

meowメソッドの引数にfalseを指定するとスーパークラスのmeowメソッドが呼び出されるので、「にゃあ」と表示される。

 ここで、試しにsuperの後の()を削除して実行してみよう(ファイル名は「sample004.rb」とする)。その場合、サブクラスのmeowメソッドに指定した引数がそのままスーパークラスのmeowメソッドに渡される。しかし、スーパークラスのmeowメソッドは引数のないメソッドなのでエラーとなる(実行例1.4)。

コンソール
$ ruby sample004.rb
がおー
sample004.rb:3:in `meow': wrong number of arguments (1 for 0) (ArgumentError)
    from sample004.rb:13:in `meow'
    from sample004.rb:20:in `<main>'
$ 
実行例1.4 superに()を付けないと、オーバーライドされたメソッドの引数がそのまま渡される

この例では、スーパークラスのmeowメソッドは引数を取らない。サブクラスのmeowメソッドの引数をそのままスーパークラスのmeowメソッドに渡そうとするので、「引数の個数が異なる」というエラーが表示される。

 逆に、スーパークラスのメソッドとオーバーライドされたメソッドが同じ引数を取るのであれば、superの後に()を付けてしまうとエラーになる。ただし、スーバークラスのメソッドで引数が省略可能であれば、super()としてもエラーにはならない。

 ここで見た例では、オーバーライドしたメソッドでスーパークラスと異なる機能を提供しているが、スーパークラスの機能に何らかの機能を追加するためにメソッドをオーバーライドする場合も多い。そのような場合には、オーバーライドしたメソッドの定義の中で、最初にsuperを呼び出してスーパークラスの機能を実行するようにし、それに引き続きサブクラス独自の処理を記述するとよい。あまり意味のない例ではあるが、簡単なコードで働きを見ておこう(リスト1.4、実行例1.5)。

sample005.rb
class Cat
  def introduce
    puts "猫です"
  end
end

class Tiger < Cat
  def introduce
    super
    puts "虎でもあります"
  end
end

animal = Tiger.new
animal.introduce
リスト1.4 オーバーライドしたメソッドで機能を追加する

Catクラスのintroduceメソッドでは「猫です」と表示する。Tigerクラスのintroduceメソッドでは、まずスーパークラスのintroduceメソッドを呼び出し、その後「虎でもあります」と表示する。

コンソール
$ ruby sample005.rb
猫です
虎でもあります
$ 
実行例1.5 オーバーライドしたメソッドで機能を追加する例を実行する

「猫です」はスーパークラスのintroduceメソッドによって表示されたもので、「虎でもあります」はサブクラスのintroduceメソッドの中に記述されたputsメソッドによって表示されたものである。

 今回はメソッドをオーバーライドする方法と、スーパークラスのメソッドを呼び出す方法を紹介した。特に、super()を付けないと引数がそのままスーパークラスのメソッドに渡されることと、super()を付けるとスーパークラスのメソッドが引数なしで呼び出されることに注意しよう。

まとめ

 メソッドをオーバーライドすれば、同じ名前のメソッドで、スーパークラスのメソッドとは異なる機能をサブクラスで提供したり、スーパークラスのメソッドにサブクラスで機能を追加したりできる。superを使えばオーバーライドされたメソッドからスーパークラスのメソッドが呼び出せる。

処理対象:継承 カテゴリ:文法 > クラス
処理対象:オーバーライド カテゴリ:文法 > クラス

※以下では、本稿の前後を合わせて5回分(第12回~第16回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

Ruby TIPS
12. クラスとそのコンストラクター/アクセサー/メソッドを定義し利用するには?

Rubyプログラミングの基本中の基本として、クラスの定義から、そのインスタンスの作成・利用、インスタンスメソッドの定義、変数へのアクセスまでを説明する。

Ruby TIPS
13. クラスを継承するには? メソッドの呼び出しをprivate/protectedで制限するには?

オブジェクト指向言語の特長である「クラスの継承」をRubyで実現する方法を解説。スーパークラスのメソッドの呼び出し制限で、Ruby言語特有の内容についても紹介する。

Ruby TIPS
14. 【現在、表示中】≫ クラスのメソッドをオーバーライドするには?

継承先クラスの新メソッドで元クラスの既存メソッドをオーバーライドして異なる機能に置き換える方法と、新メソッド内から既存メソッドを呼び出すことで既存機能に新機能を追加する方法を説明する。

Ruby TIPS
15. 範囲式や正規表現を使うには? ― 穴埋め問題と株価診断プログラムを作る

範囲式は条件式の中で使うとちょっと面白い動作をする。範囲式を使って簡単な穴埋め問題や株価診断プログラムを作ってみよう。

Ruby TIPS
16. RSSを扱うには? ― 標準rssライブラリ利用して天気予報を表示する

Rubyに標準搭載されているrssライブラリを使って、Webサイトで提供されているRSS/Atomフィードを処理する方法を説明する。例として天気予報情報のRSSフィードを使う。

サイトからのお知らせ

Twitterでつぶやこう!