こんにちは!じゃいごテックのあつしです。
オブジェクト指向プログラミング第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
継承とオーバーライドを使うと、似たような性質を持つクラスを効率よく定義出来ますね!