Programming 3 / Java

トップに戻る

クラスの継承・super・this

既存クラスを拡張する継承の仕組みと、親子のコンストラクタがどの順序で呼び出されるかを学び、IS-AとHAS-Aを使い分けます。

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 に直接書かれていない widthheightsetSize()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;
}
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
面積: 16200
NamedRectanglewidthheightgetArea() は定義されていませんが、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

確認問題

  1. スーパークラスの引数付きコンストラクタを呼ぶには何を使いますか。
  2. super() を省略したとき、自動的に呼ばれるのはどのコンストラクタですか。
  3. 「自動車はエンジンを持つ」はIS-AとHAS-Aのどちらですか。
解答を確認する
  1. super(...) をコンストラクタの先頭で使います。
  2. スーパークラスの引数なしコンストラクタ super() です。
  3. HAS-A関係です。通常は継承ではなく合成で表します。