extends で継承する
同じメソッドを、クラスの数だけ書き写している
自転車のクラスにも自動車のクラスにも start() と stop() を書き、あとから「停止するときにログを残したい」となったとき、すべてのクラスを開いて同じ修正を入れることになります。クラスが 3 つなら 3 か所、10 個なら 10 か所です。
共通の部分だけを 1 つのクラスに追い出して、残りのクラスがそれを引き継ぐ。この仕組みが 継承 です。オブジェクト指向の三大要素はカプセル化・継承・ポリモーフィズムの 3 つで、カプセル化は前のコースで済んでいます。この章では残りの 2 つを順に扱います。
extends と書くと、親の中身が流れ込む
親を指定するのは、クラス名のうしろに extends 親クラス名 と書くだけです。
Java
class Vehicle {
public String start() {
return "発進します";
}
}
class Bicycle extends Vehicle {
public String ringBell() {
return "チリンチリン";
}
}Bicycle の中身は ringBell() だけですが、次の 2 行はどちらも動きます。
Java
Bicycle b = new Bicycle();
System.out.println(b.start()); // 発進します。Vehicle から引き継いだ
System.out.println(b.ringBell()); // チリンチリン。自分で書いたextends Vehicle と書いた時点で、Vehicle の public と protected のメンバーが Bicycle からも呼べるようになります。private なものだけは引き継がれず、親の内部からしか触れません。
親を書き換えれば、子はそろって新しい振る舞いになります。最初に挙げた「10 か所を直す」問題が、1 か所で済むようになるということです。
「持っている」を継承で書かない
継承を使ってよいかは、「子は親の一種である」と日本語にして不自然でないか で決まります。
- 自転車は乗り物の一種である → 自然なので
Bicycle extends Vehicleでよい - 自動車はエンジンの一種である → 不自然。車はエンジンを 持っている だけなので、
Engineはフィールドとして持たせる
「持っている」を継承で表すと、子クラスに関係のないメソッドがぞろぞろ生えて、あとから外せなくなります。フィールドを 1 つ増やしたいだけなら、それは継承ではありません。
なお親にできるクラスは 1 つだけで、extends A, B とは書けません。extends を書かないクラスも、実は自動的に Object を継承しています。どんなクラスでも toString() が呼べるのはそのためです。
やってみよう
dogBark() を完成させてください。作るクラスは 2 つです。
- 親になる
Animalクラスを用意する。共通の振る舞いを 1 つ持たせておけば十分です AnimalをextendsしたDogクラスを作り、public String bark()を持たせるbark()は"Wan!"を返すdogBark()の中でDogのインスタンスを作り、bark()の結果をそのまま返す
dogBark() の中でいきなり return "Wan!"; と書いてもテストは通ってしまいますが、それでは継承を 1 文字も使っていません。必ず Dog extends Animal の形を作り、そのインスタンス経由で呼んでください。
判定は完全一致です。"wan!" のように小文字で始めたり、末尾の ! を落としたりすると通りません。
要件
- 親クラス
Animalと、それをextendsした子クラスDogの 2 つを定義すること Dogクラスにpublic String bark()メソッドを実装し、"Wan!"を返すことSolution.dogBark()の中でnew Dog()してからbark()を呼び、その結果をそのままreturnすること
入出力例
dogBark() → "Wan!"