ポリモーフィズムとは

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

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

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

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

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

オーバーライド

オーバーライドとは

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

オーバーライドの条件

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

アノテーション

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

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

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

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

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

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

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

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

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

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

「super」を使うと、サブクラスからスーパークラスのメソッドを直接実行することもできます。

@Override
修飾子 データ型 メソッド(引数) {
    //処理
    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

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

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

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

「Wizard」「Warrior」クラスは異なるクラスですが、「Character」クラスを継承しているので、「Character」として扱うことができます。

Character[]にまとめる

「Wizard」「Warrior」のインスタンスを、Character[] データで初期化できます。

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

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

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

[Lcharacter.Character;@xxxxx

foreach(拡張for文)

配列(コレクション)データを繰り返す方法の1つに、**foreachと呼ばれる 拡張 for文があります。

foreach
for (データ型 一時変数 : コレクション) {
	//処理
}

foreachの例

Character型の配列「characters」を繰り返し処理する場合です。 forを入力してコードアシスタントで「foreach」を選択します。

Character[]データを自動認識してfor文が入力できました。インデックスを利用しないので、通常のfor文よりもシンプルです。

for (Character character : characters) {

}

ポリモーフィズムで処理

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();
		
	for (Character character : characters) {
		System.out.println(character.name + "の攻撃");
		character.attack(monster1);
		System.out.println(monster1.isAlive());
	}

}
結果

「Wizard」は「Character」のattack()、「Warrior」は「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("集団行動");
	}

}