ポリモーフィズムとは

オブジェクト指向の三大要素

ポリモーフィズムは多態性、多様性などと呼ばれ、オブジェクト指向の三大要素の1つです。

  • 継承
  • カプセル化
  • ポリモーフィズム

オーバーロードとオーバーライド

オーバーロードやオーバライドはポリモーフィズムにあたり、同じメソッドでも異なる動作をするのがポリモーフィズムの大きな特徴です。

オーバーライド

オーバーライドとは

スーパークラスで定義したメソッドを、サブクラス再定義することをオーバーライド(Override) といいます。オーバーライドは、サブクラスでスーパークラスのメソッドを上書きしたり、機能追加して実行できます。

オーバーライドの条件

  • 引数の型、数、順番が同じ
  • 原則、戻り値の方が同じ
  • アクセス修飾子がスーパークラスの公開範囲内に収まっている

アノテーション

メソッドやプロパティ定義の前に @ で記述する記法をアノテーションといいます。アノテーションは、Javaで明示的な機能を宣言する記法で、Javaフレームワークでは多数のアノテーションがあります。

アノテーションの例(Java標準)

アノテーション 説明
@Override オーバーライドの定義
@Deprecated クラスやメンバーなどが非推奨
@SuppressWarnings コンパイラー警告を抑制
@SafeVarargs 可変長引数の型安全を宣言
@FunctionalInterface 関数型インタフェースの定義

オーバーライドメソッドの定義

スーパークラスと同じメソッドを定義します。メソッドの前には @Overrideをつけます。

@Override
修飾子 データ型 メソッド(引数) {
    //処理
}

オーバーライドのコンパイルエラー

オーバーライドは、スーパークラスと同じメソッドにしなければいけません。メソッド名、戻り値、引数が違うと、コンパイルエラーになります。

スーパークラス
サブクラスクラス

スーパークラスのメソッド実行

super. を使うと、スーパークラスのメソッドやプロパティにアクセスできます。

super.メソッド();

super.プロパティ;

オーバーライドの例

Warriorクラスで、attack() メソッドをオーバーライドしてみましょう。 Warriorの攻撃では、1/5の確率で攻撃力を倍にする仕様に変更します。

Warrior.java
	@Override
	public void attack(Monster monster) {
		System.out.println("オーバーライド");

		//元の攻撃力をキャッシュ
		int attackPower = this.attackPower;

		// 1/5の確率で攻撃力を倍にする
		Random rand = new Random();
		if (rand.nextInt(5) == 0) this.attackPower = attackPower * 2;

		// スーパークラスのattack() を実行
		super.attack(monster);

		// 攻撃力を元に戻す
		this.attackPower = attackPower;
	}

1/5の確率で、攻撃力が変わることを確認してみましょう。

結果
オーバーライド
-14
false

ポリモーフィズムのメリット

継承とポリモーフィズムをうまく使うと、コードをまとめて可読性がよくなるメリットがあります。

複数のクラスを同時に扱う

WizardWarriorクラスは異なるクラスですが、Characterクラスを継承しているので、Characterオブジェクトの Array型として操作できます。

Character[]

WizardWarriorのインスタンスを、Character[] の配列で初期化します。

App.java
Wizard wizard1 = new Wizard("アリス");
Wizard wizard2 = new Wizard("テリー");
Warrior warrior1 = new Warrior("ボブ");

// Array型に Characterのオブジェクトで初期化
Character[] characters = { wizard1, wizard2, warrior1 };
System.out.println(characters);

コンソールで確認すると、Characterクラスとして追加されています。

結果
[Lcharacter.Character;@xxxxx

ポリモーフィズムで処理

foreach で処理

Character[] データを、foreachで繰り返し表示してみましょう。

App.java
public static void main(String[] args) {
	Wizard wizard1 = new Wizard("アリス");
	Wizard wizard2 = new Wizard("テリー");
	Warrior warrior1 = new Warrior("ボブ");

	Character[] characters = { wizard1, wizard2, warrior1 };
    System.out.println(characters);

	System.out.println("--- Battle ---");
	Monster monster1 = new Monster();

    // foreach
	for (Character character : characters) {
		System.out.println(character.name + "の攻撃");
		character.attack(monster1);
		System.out.println(monster1.isAlive());
	}

}

動作確認

Wizardは自分のクラスのattack()Warriorはオーバーライドしたattack() が実行されます。

結果
--- Battle ---
アリスの攻撃
true
テリーの攻撃
true
ボブの攻撃
オーバーライド
false

演習

問題1

ポリモーフィズムの説明として正しいのはどれですか?

  1. 同じメソッド名でも、異なる動作を実行
  2. 共通のメソッドやプロパティを1つのクラスにまとめ、引き継ぐ
  3. オブジェクトを隠蔽して、外部からデータ操作を拒否してデータの不整合を防ぐ
  4. 抽象メソッドと呼ばれ、メソッドの仕様のみを定義したもの

問題2

クラスに「@Override」のように、「@」でコーディングする記法をなんといいますか?

  1. パッケージ
  2. アノテーション
  3. スーパークラス
  4. サブクラス

問題3

「Dog」「Cat」のwalk() をfor文実行してみましょう。

Animal.java
package zoo;

public class Animal {

	public String type;
	public String name;
	public String crying;

	public Animal(String name) {
		super();
        this.name = name;
	}

	public void walk() {
		String message = this.name + "が歩いた";
		System.out.println(message);
	}

	public void cry() {
		System.out.println(this.crying);
	}

	public void escape() {
		String message = this.name + "が逃げた";
		System.out.println(message);
	}
}
Cat.java
package zoo;

public class Cat extends Animal {

	public Cat(String name) {
		super(name);
		this.crying = "にゃー!";
	}

    @Override
	public void walk() {
		System.out.println(this.name + "が警戒して歩いてます。");
	}

	public void actionSolo() {
		System.out.println("単独行動");
	}
}
Dog.java
package zoo;

public class Dog extends Animal {

	public Dog(String name) {
		super(name);
		this.crying = "わん!";
	}

    @Override
	public void walk() {
		System.out.println(this.name + "が楽しそうに歩いてます。");
	}

	public void actionCollective() {
		System.out.println("集団行動");
	}

}