Rubyのオブジェクト指向におけるクラスとモジュール、継承、Mixin、アクセス制御の使い方若手エンジニア/初心者のためのRuby 2.1入門(7)(3/5 ページ)

» 2014年08月28日 18時00分 公開
[著:麻田優真、監修:山根剛司株式会社アジャイルウェア]

クラスの「継承」

 クラスの「継承」と呼ばれる機能を使うと、あるクラスの機能を受け継いだ新たなクラスを作成できます。継承先のクラスは継承元のクラスのインスタンス変数やメソッドを、そっくりそのまま使えます。

Rabbitクラスを継承する

 rabbit.rbをさらに充実させていきましょう。以下の例は、Rabbitクラスに、耳をターミナルに出力する「Rabbit#print_ears」メソッドを追加し、Rabbitクラスを継承したLopEarクラスを定義する例です。

class Rabbit
  attr_accessor :name
  attr_reader :color, :length_of_ears
 
  def initialize(name: "usachan", color: :white, length_of_ears: 10)
    @name = name
    @color = color
    @length_of_ears = length_of_ears
  end
 
  def jump
    puts "pyon! pyon!"
  end
 
  def pound_steamed_rice_into_rice_cake
    puts "pettan! pettan!"
  end
 
  def say_name
    puts "Hello, I'm #{@name}!"
  end
 
  def print_ears
    puts "∩_∩"
  end
end
 
class LopEar < Rabbit
  def print_ears
    puts "∪ ̄∪"
  end
end
rabbit.rb
require_relative "rabbit"
 
rabbit = Rabbit.new
lop = LopEar.new(name: "lopchan")
 
[rabbit, lop].each do |r|
  r.say_name
  r.print_ears
end
main04.rb

継承の表し方

 rabbit.rbの28行目以降が、たれ耳のウサギを表すLopEarクラスを定義している部分です。垂れ耳のウサギは、ウサギの一般的な性質を持っていることが期待されるので、継承で実装することにしました。「class LopEar < Rabbit」というように書くと、「そのクラス定義はRabbitクラスを継承したLopEarクラスの定義である」という意味になります。

メソッドの「上書き」。オーバーライド

 LopEarクラスの中では、「print_ears」というメソッドを定義しています。継承先のクラスで新たなメソッドを定義することもできますし、この例のように継承元のクラスに定義されているメソッドを上書きすることもできます。このように、継承元のクラスのメソッドを上書きすることを、メソッドの「オーバーライド」と呼びます。

 main04.rbでは、LopEarクラスのオブジェクトの振る舞いについて確かめています。3行目と4行目でRabbitクラスのオブジェクトとLopEarクラスのオブジェクトを生成し、6行目から9行目で、それぞれのオブジェクトのsay_nameメソッドとprint_earsメソッドを実行しています。

継承した結果

 では、main04.rbを実行してみましょう。

$ ruby main04.rb 
Hello, I'm usachan!
∩_∩
Hello, I'm lopchan!
∪ ̄∪
main04.rbの実行結果

 LopEarクラスはRabbitクラスを継承しているので、LopEarクラスのオブジェクトは、nameといったインスタンス変数やsay_nameといったメソッドを利用できます。また、上書きしたprint_earsメソッドを実行した場合は、LopEarクラス側での定義が利用されます。

Objectクラスとsuperclassメソッド

 ここまで、何げなくRabbitクラスを定義して、そのオブジェクトを利用してきましたが、実はRabbitクラスはRubyの組み込みクラスであるObjectクラスを継承しています。rabbit.rbと同じ階層のディレクトリでpryを起動して確かめてみましょう。

[1] pry(main)> require_relative "rabbit"
=> true
[2] pry(main)> Rabbit.superclass
=> Object

 [1]ではrequire_relativeを使ってrabbit.rbを読み込んでいます。また、[2]のようにクラスに対して「superclass」メソッドを使うと継承元の親クラスを得ることができ、Objectクラスであることが分かります。Objectクラスにはオブジェクトに共通する基本的なメソッドが定義されており、例えば、to_sメソッドもその一部です。

[3] pry(main)> Rabbit.new.to_s
=> "#<Rabbit:0x007fb164a72ed0>"

 [3]では、Rabbitクラスのオブジェクトを生成して、「to_s」メソッドを呼び出しています。rabbit.rbにto_sメソッドの定義がないのに、きちんと文字列が返っていることに注意してください。これは、Objectクラスでto_sが定義されているからです。

オーバーライドをしてみよう

 ここで、Arrayクラスのオブジェクト、つまり配列について、to_sの動作を見てみましょう。

[4] pry(main)> ["Alice", "in", "Wonderland"].to_s
=> "[\"Alice\", \"in\", \"Wonderland\"]"

 配列の場合は、配列の内容が人間に分かりやすい文字列として出力されています。これは、Arrayクラスでto_sメソッドをオーバーライドしているからです。Rabbitクラスでもto_sメソッドをオーバーライドしてみましょう。

class Rabbit
  attr_accessor :name
  attr_reader :color, :length_of_ears
 
  def initialize(name: "usachan", color: :white, length_of_ears: 10)
    @name = name
    @color = color
    @length_of_ears = length_of_ears
  end
 
  def jump
    puts "pyon! pyon!"
  end
 
  def pound_steamed_rice_into_rice_cake
    puts "pettan! pettan!"
  end
 
  def say_name
    puts "Hello, I'm #{@name}!"
  end
 
  def print_ears
    puts "∩_∩"
  end
 
  def to_s
    "名前: #{@name}, 毛の色: #{@color}, 耳の長さ: #{@length_of_ears}"
  end
end
 
class LopEar < Rabbit
  def print_ears
    puts "∪ ̄∪"
  end
end
rabbit.rb
[1] pry(main)> require_relative "rabbit"
=> true
[2] pry(main)> Rabbit.new.to_s
=> "名前: usachan, 毛の色: white, 耳の長さ: 10"
[3] pry(main)> LopEar.new(name: "pyonkichi", color: :brown, length_of_ears: 5).to_s
=> "名前: pyonkichi, 毛の色: brown, 耳の長さ: 5"
pry上での実行結果

 [1]でrabbit.rbを読み込み、[2]および[3]でRabbitクラスのオブジェクトとLopEarクラスのオブジェクトを生成し、to_sメソッドを呼び出しています。ここでは、to_sメソッドをオーバーライドしているので、きちんと名前などの情報が文字列として返っています。

クラス変数・クラス定数と継承

 ここまでの例ではインスタンス変数のみを使ってきましたが、クラス変数やクラス定数といったものを使うこともできます。

 クラス変数は、その名の通りクラス自体に設定できる変数で、継承先のクラスから参照や代入が可能で、クラスのオブジェクトからでも参照や代入ができます。

 クラス定数も、クラス変数と同様にクラス自身に設定される定数です。クラス定数はクラスの外から参照することもできます。

 では、RabbitクラスとLopEarクラスにクラス変数とクラス定数を追加してみましょう。

class Rabbit
  attr_accessor :name
  attr_reader :color, :length_of_ears
 
  @@count = 0
 
  DEFAULT_NAME = "usachan"
  DEFAULT_COLOR = :white
  DEFAULT_LENGTH_OF_EARS = 10
 
  DESCRIPTION = "ウサギは特徴的な耳を持つ可愛い動物です。"
 
  def initialize(name: DEFAULT_NAME, color: DEFAULT_COLOR, length_of_ears: DEFAULT_LENGTH_OF_EARS)
    @name = name
    @color = color
    @length_of_ears = length_of_ears
    @@count += 1
  end
 
  def jump
    puts "pyon! pyon!"
  end
 
  def pound_steamed_rice_into_rice_cake
    puts "pettan! pettan!"
  end
 
  def say_name
    puts "Hello, I'm #{@name}!"
  end
 
  def print_ears
    puts "∩_∩"
  end
 
  def print_description
    puts "#{DESCRIPTION}"
  end
 
  def to_s
    "名前: #{@name}, 毛の色: #{@color}, 耳の長さ: #{@length_of_ears}, 全体でのウサギの数: #{@@count}"
  end
end
 
class LopEar < Rabbit
  DESCRIPTION = "ロップイヤーはウサギであり、耳が垂れている品種の総称です。"
 
  def print_ears
    puts "∪ ̄∪"
  end
 
  def print_class_constants_and_variable
    puts "DEFAULT_NAME: #{DEFAULT_NAME}, DEFAULT_COLOR: #{DEFAULT_COLOR}, DEFAULT_LENGTH_OF_EARS: #{DEFAULT_LENGTH_OF_EARS}"
    puts "DESCRIPTION: #{DESCRIPTION}"
    puts "@@count: #{@@count}"
  end
end
rabbit.rb

 rabbit.rbの5行目の「@@count = 0」の部分が、Rabbitクラスのクラス変数「count」に初期値「0」を設定している部分です。また、7行目から11行目にかけて、クラス定数をいくつか設定しています。クラス定数は、一般的な定数と同様に大文字から始まります。

 「@@count」は、「Rabbit#initialize」メソッドが呼ばれたとき、つまりオブジェクトが生成されたときに1を足しています(17行目)。オブジェクトが生成されるたびに1ずつ値が増えるので、全体のウサギの数を表せるというわけです。

 また、クラス定数の性質を確認するために、36行目から38行目にかけて、「Rabbit#print_description」というメソッドを定義しました。このメソッドは、単純にクラス定数DESCRIPTIONを標準出力に出力するだけの簡単なものです。

 また、46行目にLopEarのクラス定数として、Rabbitクラスのクラス定数DESCRIPTIONを上書きし、52行目から56行目にかけて、一連のクラス定数やクラス変数を標準出力に出力するメソッド、「LopEar#print_class_constants_and_variable」を定義しました。

クラス変数・クラス定数の動きを確認

require_relative "rabbit"
 
rabbit = Rabbit.new
puts rabbit.to_s
 
lop = LopEar.new
puts lop.to_s
 
lop.print_class_constants_and_variable
 
rabbit.print_description
lop.print_description
main05.rb

 main05.rbは、これら一連のクラス定数とクラス変数の動きを確認するコードです。以下に、その出力結果を示します。

$ ruby main05.rb
 
名前: usachan, 毛の色: white, 耳の長さ: 10, 全体でのウサギの数: 1
名前: usachan, 毛の色: white, 耳の長さ: 10, 全体でのウサギの数: 2
DEFAULT_NAME: usachan, DEFAULT_COLOR: white, DEFAULT_LENGTH_OF_EARS: 10
DESCRIPTION: ロップイヤーはウサギであり、耳が垂れている品種の総称です。
@@count: 2
ウサギは特徴的な耳を持つ可愛い動物です。
ウサギは特徴的な耳を持つ可愛い動物です。
main05.rbの出力結果

 3行目でRabbitクラスのオブジェクトを変数「rabbit」に代入し、4行目でrabbitオブジェクトに対して「to_s」メソッドを呼び出しています。また、6行目でLopEarクラスのオブジェクトを変数「lop」に代入し、7行目で「to_s」メソッドを呼び出しています。クラス変数は継承されても共有され、2回目の「to_s」ではウサギの数が1増えていることが分かります。

 また9行目では、「LopEar#print_class_constants_and_variable」を呼び出して、一連のクラス変数やクラス定数を出力しています。rabbit.rbの46行目でクラス定数「DESCRIPTION」を上書きしているので、「DESCRIPTION」にはロップイヤーの説明が設定されていることが分かります。

 ただし注意したいのは、12行目のように、継承先のクラスのオブジェクトが継承元のオブジェクトのメソッドを呼ぶ場合です。Rabbitクラスの中で定義されている「print_description」メソッドはクラス定数「DESCRIPTION」を使いますが、継承元のRabbitクラスの「DESCRIPTION」が使われていることに留意してください。

Copyright © ITmedia, Inc. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。