基礎

Rubyの継承入門|クラスの再利用とオーバーライドの方法

継承はオブジェクト指向プログラミングの重要な概念で、既存のクラス(親クラス)の機能を引き継いで新しいクラス(子クラス)を作成する仕組みです。共通する機能を親クラスにまとめ、差分だけを子クラスで定義することで、コードの重複を減らせます。Rubyでは<記号を使って継承関係を定義します。

基本的な使い方

Ruby
# 親クラス
class Animal
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def speak
    puts "#{@name}が鳴いています"
  end

  def info
    puts "動物: #{@name}"
  end
end

# 子クラス
class Dog < Animal
  def speak
    puts "#{@name}: ワンワン!"
  end
end

class Cat < Animal
  def speak
    puts "#{@name}: ニャー!"
  end
end

dog = Dog.new('ポチ')
cat = Cat.new('タマ')

dog.speak   # オーバーライドされたメソッド
cat.speak
dog.info    # 親クラスから継承したメソッド
cat.info
実行結果
ポチ: ワンワン!
タマ: ニャー!
動物: ポチ
動物: タマ

class Dog < AnimalDogAnimalを継承しています。DogAnimalinitializeinfoメソッドをそのまま使えます。speakメソッドは子クラスで再定義(オーバーライド)されているため、子クラス独自の動作をします。

superで親クラスのメソッドを呼ぶ

superを使うと、オーバーライドしたメソッド内から親クラスの同名メソッドを呼び出せます。

Ruby
class Vehicle
  attr_reader :name, :speed

  def initialize(name, speed)
    @name = name
    @speed = speed
  end

  def info
    "#{@name}(速度: #{@speed}km/h)"
  end
end

class Car < Vehicle
  def initialize(name, speed, seats)
    super(name, speed)  # 親クラスのinitializeを呼ぶ
    @seats = seats
  end

  def info
    super + "(座席数: #{@seats})"  # 親クラスのinfoを呼んで拡張
  end
end

class Bicycle < Vehicle
  def initialize(name)
    super(name, 20)  # 速度は固定で親クラスに渡す
  end
end

car = Car.new('セダン', 180, 5)
bike = Bicycle.new('ロードバイク')

puts car.info
puts bike.info
実行結果
セダン(速度: 180km/h)(座席数: 5)
ロードバイク(速度: 20km/h)

super(name, speed)で親クラスのinitializeに引数を渡しています。superを引数なしで呼ぶと、現在のメソッドと同じ引数が自動的に親メソッドに渡されます。

is_a?で型チェック

Ruby
class Animal; end
class Dog < Animal; end
class Cat < Animal; end

dog = Dog.new

puts dog.is_a?(Dog)     # true
puts dog.is_a?(Animal)  # true(親クラスも含む)
puts dog.is_a?(Cat)     # false
puts dog.class          # Dog
実行結果
true
true
false
Dog

実践的な使い方

Ruby
class Shape
  attr_reader :color

  def initialize(color = '黒')
    @color = color
  end

  def area
    0  # サブクラスでオーバーライドする
  end

  def to_s
    "#{self.class.name}(色: #{@color}、面積: #{area})"
  end
end

class Circle < Shape
  def initialize(radius, color = '黒')
    super(color)
    @radius = radius
  end

  def area
    (3.14159 * @radius ** 2).round(2)
  end
end

class Rectangle < Shape
  def initialize(width, height, color = '黒')
    super(color)
    @width = width
    @height = height
  end

  def area
    @width * @height
  end
end

shapes = [
  Circle.new(5, '赤'),
  Rectangle.new(10, 6, '青'),
  Circle.new(3),
  Rectangle.new(4, 4, '緑')
]

shapes.each { |s| puts s }
puts "---"
total = shapes.sum(&:area)
puts "合計面積: #{total.round(2)}"
実行結果
Circle(色: 赤、面積: 78.54)
Rectangle(色: 青、面積: 60)
Circle(色: 黒、面積: 28.27)
Rectangle(色: 緑、面積: 16)
---
合計面積: 182.81
Rubyは単一継承

Rubyでは1つのクラスは1つの親クラスのみ継承できます(単一継承)。複数の機能を組み合わせたい場合はモジュールのinclude(Mixin)を使います。これにより多重継承の問題を避けつつ、柔軟な機能拡張が可能です。

継承の深さに注意

継承階層が深くなりすぎると、コードの理解やデバッグが困難になります。一般的に3階層以上の継承は避け、コンポジション(クラスを組み合わせる方法)を検討しましょう。「is-a」(〜である)関係のときに継承を使うのが適切です。

まとめ

  • class 子クラス < 親クラスで継承関係を定義する
  • 子クラスは親クラスのメソッドをすべて引き継ぐ
  • 子クラスで同名メソッドを定義するとオーバーライドになる
  • superで親クラスのメソッドを呼び出せる
  • Rubyは単一継承で、多重継承にはモジュールを使う