Class (vol.4 継承)

  • クラスを「種類別に階層を作って分類する」 → 「継承」という仕組みを利用できる
    • ドリンク, フード などの種類別
      • ドリンク > コーヒー, 紅茶, スムージー のような階層

クラスの継承

商品 > 飲み物 の階層をクラス定義する場合

  • まず商品を生成するクラス Item を定義する
class Item
  def name
    @name
  end
  def name=(text)
    @name = text
  end
end
  • 次にドリンクを生成するクラス Drink を定義する
class Drink
  def name
    @name
  end
  def name=(text)
    @name = text
  end
  def size
    @size
  end
  def size=(text)
    @size = text
  end
end
  • 上記の場合、下記の部分が重複した記述となる
    1. インスタンス変数を戻り値として返す name メソッド
    2. 引数から受け取った名前を、インスタンス変数に代入する name= メソッド

こういったケースの場合、クラス Drink の上流(親)となるクラス Item で定義されたメソッドを、下流(子)となるクラスに引き継ぐことができる → クラスの継承

継承クラスの定義方法

class クラス名 < スーパークラス
end
  • Drink クラスでは name および name= メソッドは定義していないが
  • class Drink < Item によって、Drink クラスは Item クラスに定義されたすべてのメソッドを呼び出せる
class Item
  def name
    @name
  end
  def name=(text)
    @name = text
  end
end

class Drink < Item
  def size
    @size
  end
  def size=(text)
    @size = text
  end
end

drink = Drink.new

drink.name = "coffee" # (*)
drink.size = "small"

p "#{drink.name} : #{drink.size} size"
# "coffee : small size"
  • Item クラス
    • 継承元となるクラス
    • スーパークラス(親クラス)
  • Drink クラス
    • 継承先となるクラス
    • スーパークラス(子クラス)

親クラスと子クラスのどちらにメソッドを加えるか

上記の例に対して、さらなるメソッドを追加する場合

  • 価格 を設定・取得するメソッド
    • Drink だけに限定されず、(例えば Food など)商品全般に共通する
      • 親クラス Item にメソッドを定義するべき
  • ホット or アイス を設定・取得するメソッド
    • Drink に必要なメソッドでも、(例えば Food など)商品全般に共通するとは限らない
      • 子クラス Drink にメソッドを定義するべき

Ruby に用意されているクラスたちの継承関係を確認 ancestors

  • 整数クラス Integer や小数クラス Float は、数値クラス Numeric を継承して作られている
    • 例)数値が 0 か判定する zero? メソッドは、親クラス Numeric に定義されている
      • 親クラスのメソッドを継承している整数クラス・小数クラスともにこのメソッドを実行できる
p 0.class # Integer
p 0.0.class # Float

p 0.zero? # true
p 0.0.zero? # true
  • そのクラスの継承関係を見るには ancestors メソッドを使う
    • クラスの家系図を確認するメソッド
    • 継承関係上の祖先を辿ることができる
  • 正確には、「親クラスと include しているモジュールを表示する」メソッド
p String.ancestors  # [String, Comparable, Object, Kernel, BasicObject]
p Integer.ancestors # [Integer, Numeric, Comparable, Object, Kernel, BasicObject]
p Float.ancestors   # [Float, Numeric, Comparable, Object, Kernel, BasicObject]
p Array.ancestors   # [Array, Enumerable, Object, Kernel, BasicObject]

class Parent
end
class Child < Parent
end

p Parent.ancestors  # [Parent, Object, Kernel, BasicObject]
p Child.ancestors   # [Child, Parent, Object, Kernel, BasicObject]
  • どのクラスの祖先にも Object, Kernel, BasicObject がある
    • これらのクラスやモジュールによって、オブジェクトとしての基礎となる動作が提供されている

親子クラスで同名のメソッドが存在する場合の挙動

class Item
  def name
    @name
  end
  def name=(text)
    @name = text
  end
  def full_name
    @name
  end
end

class Drink < Item
  def size
    @size
  end
  def size=(text)
    @size = text
  end
  def full_name
    "#{@name} : #{@size}"
  end
end

item = Item.new
drink = Drink.new

item.name = "apple"

drink.name = "coffee"
drink.size = "small"

p item.full_name # "apple"
p drink.full_name # "coffee : small"
  • 親子クラスで同名のメソッドが存在する場合、自分のクラスのメソッドが呼ばれる
  • 正確には、継承関係を上流へ辿っていき、最初に該当したメソッドを呼び出す
  • 親クラスの同名メソッドは呼ばれない

親クラスのメソッドを呼び出す super

  • 上記の例は「親クラスの同名メソッドは呼ばれない」だけであり、上書きしている訳ではない
  • 子クラスの中で親クラスのメソッドを呼び出す場合
    • メソッドの中で super と記述する
class Item
  def name
    @name
  end
  def name=(text)
    @name = text
  end
  def full_name
    @name
  end
end

class Drink < Item
  def size
    @size
  end
  def size=(text)
    @size = text
  end
  def full_name
    super #
  end
end

drink = Drink.new
drink.name = "coffee"
drink.size = "small"

p drink.full_name # "coffee"
  • Drink クラスの full_name メソッドで、Item クラスの full_name メソッドを呼び出している
  • super を実行すると、親クラスの同名メソッドが呼び出され、戻り値として返す