オブジェクト指向の初学者でもわかるポリモーフィズム

# ポリモーフィズムとは

ウィキペディアにはこう書いてありました。

>ポリモーフィズム（英: Polymorphism）とは、プログラミング言語の型システムの性質を表すもので、プログラミング言語の各要素（定数、変数、式、オブジェクト、関数、メソッドなど）についてそれらが複数の型に属することを許すという性質を指す。ポリモルフィズム、多態性、多相性、多様性とも呼ばれる。

「プログラミング言語の各要素についてそれらが複数の型に属する」という点が難しいですが。

### 要するに、ポリモーフィズムとは、共通メインルーチンのことです。

#### 共通メインルーチンとは？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/409243/f39dc457-2d77-6fb2-f7d6-14bcf5d192d4.png "オブジェクト指向でなぜつくるのか 第2版 より引用")

+ 共通サブルーチンと逆の発想。
+ 呼び出し方は一通りだが、呼び出されるもの（メソッド）は複数通りある。

# なぜポリモーフィズムを使うのか
+ 呼び出される側（クラス、メソッド）が増えても、呼び出し元のコードは変えなくていい。
+ つまり、プログラムの修正やテストにかかるコストを減らせる。


# ポリモーフィズムの実装

+ 通常、生成したインスタンスを変数に代入する場合、その変数の型は生成したインスタンスと合わせる必要があります。

```java

Door dr = new Door();     // OK: 左辺と右辺の型が一致（注：Doorは自作クラス）
Door dr2 = new String();  // NG: 左辺と右辺の型が不一致なのでコンパイルエラー
```

+ ポリモーフィズムを使うと、左辺と右辺の型が異なっていても、代入できます。
    + 正確にいうと、その２つのクラスは継承関係である必要があります。

```java
ClassA a = new ClassB();     // OK
```
```java
// ClassAがスーパークラス、ClassBがサブクラス
public class ClassB extends ClassA {
    // 省略
}
```


# ポリモーフィズムの具体例
## 1. ポリモーフィズムを利用した配列

#### 呼び出し元（メインメソッドがあるクラス）

+ 処理(1)について

変数playersは「Playerクラスの配列」で宣言しています。
その配列の中身には、別のクラス(Playerクラスのサブクラス)のインスタンスを代入しています。

+ 処理(2)について

ここがポリモーフィズム（共通メインルーチン）です。
「players[X].janken()」と呼び出し方は一通りだが、呼び出されるメソッドは複数（Personのじゃんけん、Robotのじゃんけん）あります。

```java:MainClass.java
public class MainClass {

    public static void main(String[] args) {

        // (1) じゃんけんするプレイヤーを生成
        Player[] players = new Player[3];
        players[0] = new Person();  // 人
        players[1] = new Robot();   // ロボットA
        players[2] = new Robot();   // ロボットB

        // (2) 各プレイヤーが順番にじゃんけんさせる
        players[0].janken();    // 人
        players[1].janken();    // ロボットA
        players[2].janken();    // ロボットB
    }
}
```

#### 呼び出し先（スーパークラス）
```java:Player.java
public class Player {

    private int win;

    public void janken() {
        // じゃんけんの処理
    }

    public int getWin() {
        return win;
    }
}
```

#### 呼び出し先（サブクラス）
```java:Person.java
public class Person extends Player {
    // 省略
}
```
```java:Robot.java
public class Robot extends Player {
    // 省略
}
```

今後、jankenメソッドの処理が変わっても、呼び出し元は(1)も(2)も変更しなくて済みます。
また、以下のようにプログラム修正があっても、呼び出し元コードは(1)箇所だけ変えればよいです。

+ プレイヤーの人数が変更（例：3名から10名）
+ プレイヤーの種別が変更（例：人やロボットに加え、人工知能、猿、宇宙人・・・など）
    + 注意：もちろん、呼び出され側は、人工知能クラスを作るなどの変更は必要です。

## 2. 引数でポリモーフィズムを利用した例

先ほどのじゃんけんプログラムに仕様変更があったとします。
先ほどのMainClass.javaに「【追加】」のコードを追記しました。

+ Entryクラスのコンストラクタは、Playerクラスの引数を要求しています。
+ 実際には、Playerクラスのサブクラスを引数に設定できています。
+ 繰り返しになりますが、今後サブクラスが増えたとしても、呼び出し元コードの書き方は同じです。

```java:MainClass.java
public class MainClass {

    public static void main(String[] args) {

        // (1) じゃんけんするプレイヤーを生成
        Player[] players = new Player[3];
        players[0] = new Person();  // 人
        players[1] = new Robot();   // ロボットA
        players[2] = new Robot();   // ロボットB

        // 【追加】じゃんけん大会に登録する処理を追加
        Entry[] entry = new Entry[3];
        entry[0] = new Entry(players[0]);   // 人
        entry[1] = new Entry(players[1]);   // ロボットA
        entry[2] = new Entry(players[2]);   // ロボットB

        // (2) 各プレイヤーが順番にじゃんけんさせる
        players[0].janken();    // 人
        players[1].janken();    // ロボットA
        players[2].janken();    // ロボットB
    }
}
```
```java:Entry.java
public class Entry {

    private int win;

    // コンストラクタ
    public Entry(Player player) {
        // じゃんけん大会に登録するためにプレイヤーの情報（勝利数の履歴など）を取得
        win = player.getWin();
    }
}
```
# まとめ
#### ポリモーフィズムとは？
+ 共通メインルーチンです。
+ 呼び出し方は一通りの記述で統一できます。
+ 呼び出されるもの（メソッド）は複数通りあります。

### なぜポリモーフィズムを使うのか？
+ 呼び出されるものが変わっても、呼び出し元のコードは変えなくていいためです。
+ プログラムの修正やテストにかかるコストを減らせるためです。これは、実務では非常に重要な観点です。

# 参考文献
+ Head First Java 第2版 ―頭とからだで覚えるJavaの基本
+ オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向をもっと勉強したい方は一読をオススメします。
Amazonで購入できます。