1
継承とは
既存のクラスを拡張して新しいクラスを作ることを継承といいます。Javaでは extends を使います。
継承の基本形
class NamedRectangle extends Rectangle {
// NamedRectangle独自の定義
}Rectangleスーパークラス・親クラス
↓ extends
NamedRectangleサブクラス・子クラス
- 引き継ぐ親クラスの利用可能なフィールドやメソッドを使える。
- 追加する子クラス独自のフィールドやメソッドを定義できる。
- 上書きする継承したメソッドの動作を子クラス側で変更できる。
Javaのクラスが直接継承できるクラスは1つだけです。ただし、継承を何段階にもつなげることはできます。
2
Objectクラスと継承される機能
extends を書いていないクラスも、最上位の java.lang.Object を継承しています。
暗黙の継承
class Rectangle {
}
// おおむね次と同じ意味
class Rectangle extends Object {
}Rectangle.java / NamedRectangle.java
class Rectangle {
int width;
int height;
void setSize(int width, int height) {
this.width = width;
this.height = height;
}
int getArea() {
return width * height;
}
}
class NamedRectangle extends Rectangle {
String name;
}NamedRectangle に直接書かれていない width、height、setSize()、getArea() も、継承によって利用できます。
3
コンストラクタは継承されない
フィールドやメソッドとは異なり、コンストラクタはサブクラスへ継承されません。
Rectangleのコンストラクタ
Rectangle(int width, int height) {
this.width = width;
this.height = height;
}Rectangle(int, int) があっても、対応するコンストラクタを子クラスに定義しない限り、new NamedRectangle(123, 45) とは書けません。
super()で親のコンストラクタを呼ぶ
NamedRectangleのコンストラクタ
NamedRectangle(String name) {
super(200, 32);
this.name = name;
}- super() はコンストラクタの先頭に書く。
- 省略すると、引数なしの super() が自動的に呼ばれる。
- 親に引数なしコンストラクタがなければ、引数付きの super(...) を明示する。
4
this()と初期化の順序
this() を使うと、同じクラスに定義された別のコンストラクタを呼び出せます。これも必ず先頭に書きます。
NamedRectangle.java
NamedRectangle() {
this("NO NAME");
}
NamedRectangle(String name) {
super(200, 32);
this.name = name;
}new NamedRectangle()NamedRectangle()this("NO NAME")super(200, 32)親を初期化
this.name = name子を初期化
サブクラスのオブジェクトを生成すると、親クラスの初期化が終わってから子クラスの初期化が行われます。
1つのコンストラクタの先頭で super() と this() を同時には呼べません。どちらも「先頭に書く」必要があるためです。
5
そのまま動く継承サンプル
次のコードを InheritanceSample.java という名前で保存してください。1ファイルの中に親クラス、子クラス、main メソッドが揃っているため、そのままコンパイルして実行できます。
InheritanceSample.java
// スーパークラス
class Rectangle {
int width;
int height;
Rectangle(int width, int height) {
this.width = width;
this.height = height;
System.out.println("Rectangleを初期化");
}
int getArea() {
return width * height;
}
}
// サブクラス
class NamedRectangle extends Rectangle {
String name;
NamedRectangle() {
this("NO NAME", 200, 32);
}
NamedRectangle(String name, int width, int height) {
super(width, height);
this.name = name;
System.out.println("NamedRectangleを初期化");
}
void printInfo() {
System.out.println("名前: " + name);
System.out.println("幅: " + width);
System.out.println("高さ: " + height);
System.out.println("面積: " + getArea());
}
}
// 実行用クラス
public class InheritanceSample {
public static void main(String[] args) {
NamedRectangle rectangle =
new NamedRectangle("教室の黒板", 180, 90);
rectangle.printInfo();
}
}コンパイルと実行
javac InheritanceSample.java
java InheritanceSample実行結果を確認する
output
Rectangleを初期化
NamedRectangleを初期化
名前: 教室の黒板
幅: 180
高さ: 90
面積: 16200NamedRectangle に width、height、getArea() は定義されていませんが、Rectangle から継承しているため利用できます。
6
IS-A関係と継承
継承は「サブクラスはスーパークラスの一種である」という自然な関係を表します。これをIS-A関係と呼びます。
Animal動物
↓
Bird鳥は動物の一種
↓
Chickenニワトリは鳥の一種
IS-Aの例
class Bird extends Animal {
}
class Chicken extends Bird {
}7
HAS-A関係と合成
あるクラスが別のクラスのオブジェクトをフィールドとして持つ関係をHAS-A関係と呼びます。部品を組み合わせる設計は合成(Composition)です。
Car.java
class Car {
Engine engine;
Tire[] tires = new Tire[4];
}| 関係 | 読み方 | 設計 |
|---|---|---|
| Bird — Animal | 鳥は動物の一種である | IS-A:継承 |
| Car — Engine | 自動車はエンジンを持つ | HAS-A:合成 |
| Car — Tire | 自動車はタイヤを持つ | HAS-A:合成 |
単にコードを使い回すためだけに継承するのではなく、自然なIS-A関係が成立するかを確認します。成立しない場合はHAS-Aによる合成を検討しましょう。
8
継承を使う目的
- 既存機能の再利用元のクラスを変更せずに新しい機能を追加する。
- 共通化複数のクラスに共通するフィールドやメソッドを親へまとめる。
- 使い方の統一共通のメソッドを持たせ、関連するクラスを同じ考え方で扱う。
9
確認問題
- スーパークラスの引数付きコンストラクタを呼ぶには何を使いますか。
- super() を省略したとき、自動的に呼ばれるのはどのコンストラクタですか。
- 「自動車はエンジンを持つ」はIS-AとHAS-Aのどちらですか。
解答を確認する
- super(...) をコンストラクタの先頭で使います。
- スーパークラスの引数なしコンストラクタ super() です。
- HAS-A関係です。通常は継承ではなく合成で表します。