LoginSignup
2
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-11-05

ポリモーフィズムとは

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

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

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

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

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

image.png

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

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

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

ポリモーフィズムの実装

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

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で購入できます。

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3