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


ポリモーフィズムとは

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

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

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

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

共通メインルーチンとは?

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

なぜポリモーフィズムを使うのか

  • 呼び出される側(クラス、メソッド)が増えても、呼び出し元のコードは変えなくていい。
  • つまり、プログラムの修正やテストにかかるコストを減らせる。

ポリモーフィズムの実装

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

Door dr = new Door();     // OK: 左辺と右辺の型が一致(注:Doorは自作クラス)
Door dr2 = new String();  // NG: 左辺と右辺の型が不一致なのでコンパイルエラー
  • ポリモーフィズムを使うと、左辺と右辺の型が異なっていても、代入できます。
    • 正確にいうと、その2つのクラスは継承関係である必要があります。
ClassA a = new ClassB();     // OK
// ClassAがスーパークラス、ClassBがサブクラス
public class ClassB extends ClassA {
    // 省略
}

ポリモーフィズムの具体例

1. ポリモーフィズムを利用した配列

呼び出し元(メインメソッドがあるクラス)

  • 処理(1)について

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

  • 処理(2)について

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

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
    }
}

呼び出し先(スーパークラス)

Player.java
public class Player {

    private int win;

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

    public int getWin() {
        return win;
    }
}

呼び出し先(サブクラス)

Person.java
public class Person extends Player {
    // 省略
}
Robot.java
public class Robot extends Player {
    // 省略
}

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

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

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

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

  • Entryクラスのコンストラクタは、Playerクラスの引数を要求しています。
  • 実際には、Playerクラスのサブクラスを引数に設定できています。
  • 繰り返しになりますが、今後サブクラスが増えたとしても、呼び出し元コードの書き方は同じです。
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
    }
}
Entry.java
public class Entry {

    private int win;

    // コンストラクタ
    public Entry(Player player) {
        // じゃんけん大会に登録するためにプレイヤーの情報(勝利数の履歴など)を取得
        win = player.getWin();
    }
}

まとめ

ポリモーフィズムとは?

  • 共通メインルーチンです。
  • 呼び出し方は一通りの記述で統一できます。
  • 呼び出されるもの(メソッド)は複数通りあります。

なぜポリモーフィズムを使うのか?

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

参考文献

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

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