こんにちは!じゃいごテックのあつしです。
オブジェクト指向プログラミング第2弾はクラスの継承についてご紹介します。
継承は共通の性質を持ったオブジェクトに対して使われます。例えば、犬、猫、兎というクラスを作るときに共通の性質を哺乳類などとしてスーパークラスで定義(汎化)して共通化し、それぞれの特徴はそれぞれのクラスで定義(特化)する手法です。
クラスの継承
クラスは、あるクラス(スーパークラス・親クラス)の機能を引き継いだクラス(サブクラス・子クラス)を定義することができます。
前回、Appleクラスを定義した時にnewメソッドでインスタンスを生成してclassメソッドでクラスを確認しましたが、Appleクラスの中身は空だったはずですよね。
実は自動的にObjectクラスを継承しているので、Objectクラスのクラスメソッドであるnewメソッドやインスタンスメソッドのclassメソッドが使えたというわけです。
同様に組込みライブラリのクラスでも継承が行われていて、良く使うクラスの継承関係は下図のようになっています。

出典:プロを目指す人のためのRuby入門
継承の例
# 空のAppleクラス class Apple end # AppleクラスはObjectクラスのクラスメソッドnewメソッドが使える ringo = Apple.new # AppleクラスのインスタンスはObjectクラスのインスタンスメソッドclassメソッドが使える p ringo.class # > Apple # AppleクラスのスーパークラスはObjectクラス p ringo.class.superclass # > Object # ObjectクラスのスーパークラスはBasicObjectクラス p ringo.class.superclass.superclass # BasicObject # 123 は Integerクラス p 123.class # > Integer # IntegerクラスのスーパークラスはNumericクラス p 123.class.superclass # > Numeric # NumericクラスのスーパークラスはObjectクラス p 123.class.superclass.superclass # > Object
クラスを継承してみる
ロールプレイングゲームを例に継承を実装してみます。
スーパークラスをVillager(村人)クラス、サブクラスをHero(勇者)クラスとします。
Villagerクラス
- インスタンス変数
- @name : String
- @hp : Integer
- インスタンスメソッド
- greet : "こんにちは" と出力する
- attack : "#{@name} は木の棒で殴りかかった" と出力する
先ずはVillagerクラスを継承したHeroクラスを作ってみます。
class Villager
def initialize(name, hp)
@name = name
@hp = hp
end
def greet
puts "こんにちは"
end
def attack
puts "#{@name} は木の棒で殴りかかった"
end
end
# HeroクラスはVillagerクラスを継承
class Hero < Villager
end
# Villagerクラスのインスタンスを生成
villager = Villager.new("村人A", 10)
p villager
# > #<Villager:0x00007fffdd0a8248 @name="村人A", @hp=10>
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった
# Heroクラスのインスタンスを生成
hero = Hero.new("よしひこ", 20)
p hero
# > #<Hero:0x00007fffdd0a75f0 @name="よしひこ", @hp=20>
hero.greet
# > こんにちは
hero.attack
# > よしひこ は木の棒で殴りかかった
HeroクラスがVillagerクラスの特性を引き継いでいることが確認出来ました。
オーバーライド
スーパークラスで定義されたメソッドと同じ名前のメソッドをサブクラスで定義することによって、メソッドを上書きすることが出来ます。
勇者は木の棒ではなく、剣で攻撃したいので、Heroクラスのattackメソッドをオーバーライドします。
class Villager
def initialize(name, hp)
@name = name
@hp = hp
end
def greet
puts "こんにちは"
end
def attack
puts "#{@name} は木の棒で殴りかかった"
end
end
class Hero < Villager
# attackメソッドをオーバーライド
def attack
puts "#{@name} は光の剣で切りかかった"
end
end
# Villagerクラス
villager = Villager.new("村人A", 10)
p villager
# > Villager
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった
# Heroクラス
hero = Hero.new("よしひこ", 20)
p hero
# > Hero
hero.greet
# > こんにちは
hero.attack
# > よしひこ は光の剣で切りかかった
同じattackメソッドですが、VillagerクラスとHeroクラスで異なる処理を行うことが出来ました。
今回は実装していないですが、戦士だったら斧で攻撃とかになりますかね。このように同じメソッドでもクラスによって違う処理を行うことをポリモーフィズム(日本語だと多態性とか多様性)といいます。
super
super はオーバーライドしているスーパークラスのメソッドを呼び出します。カッコと引数が省略された場合は現在のメソッドの引数がそのまま渡されます。
引数を渡さずにオーバーライドしたメソッドを呼び出すにはsuper()と明示します。
initializeメソッドとgreetメソッドでsuperを使ってオーバーライドしてみます。また、Heroクラスのインスタンスメソッドmagicを定義してみます。
Heroクラス
- インスタンス変数
- @name : String (Villagerクラスから継承)
- @hp : Integer (Villagerクラスから継承)
- @mp : Integer
- インスタンスメソッド
- greet :
1行目に "こんにちは" と出力する (superでVillagerクラスから呼び出す)
2行目に "私は勇者#{@name}です!" と出力する - attack : "#{@name} は光の剣で切りかかった" と出力する
- magic : "#{@name} は魔法をとなえた" と出力する
- greet :
class Villager
def initialize(name, hp)
@name = name
@hp = hp
end
def greet
puts "こんにちは"
end
def attack
puts "#{@name} は木の棒で殴りかかった"
end
end
class Hero < Villager
# initializeメソッドをオーバーライド
def initialize(name, hp, mp)
# @name, @hp はVillagerクラスで初期化
super(name, hp)
# @mp はHeroクラスのインスタンス変数
@mp = mp
end
# greetメソッドをオーバーライド
def greet
# Villagerクラスのgreetを呼び出す
super
puts "私は勇者#{@name}です!"
end
# attackメソッドをオーバーライド
def attack
puts "#{@name} は光の剣で切りかかった"
end
# Heroクラスのインスタンスメソッドを定義
def magic
puts "#{@name} は魔法をとなえた"
end
end
# Villagerクラス
villager = Villager.new("村人A", 10)
p villager
# > #<Villager:0x00007fffc1197eb8 @name="村人A", @hp=10>
villager.greet
# > こんにちは
villager.attack
# > 村人A は木の棒で殴りかかった
# Heroクラス
hero = Hero.new("よしひこ", 20, 10)
p hero
# > #<Hero:0x00007fffc1197af8 @name="よしひこ", @hp=20, @mp=10>
hero.greet
# > こんにちは
# > 私は勇者よしひこです!
hero.attack
# > よしひこ は光の剣で切りかかった
hero.magic
# > よしひこ は魔法をとなえた
Villagerクラスの特性を再利用しつつ、Heroクラスを実装することが出来ました。
今回のまとめ
今回はクラスの継承についてご紹介しました。
- 継承(スーパークラス・サブクラス)
- オーバーライド(ポリモーフィズム)
- super

継承とオーバーライドを使うと、似たような性質を持つクラスを効率よく定義出来ますね!