Java入門20 ポリモーフィズムで柔軟に

Java入門の第20回です。前回からの続きですが、今回だけでも分かる内容になっています。このシリーズの第1回はこちらです。

今回は、ポリモーフィズムについて解説します。プログラミングにおけるポリモーフィズムとはどのような意味なのか、実例を確認しながら理解を深めましょう。

ポリモーフィズムとは

ポリモーフィズムという言葉は、あまり聞きなれないかもしれません。ポリモーフィズム(Polymorphism)を直訳すると「多形性、多型性」です。

ポリモーフィズムは生物学の分野などで使われる用語ですが、生物学では「同一種でありながら異なる遺伝子を持つ集団が複数存在している状態」を意味します。

プログラミングにおけるポリモーフィズムも、この概念と似ています。プログラミングにおけるポリモーフィズムとは、「オブジェクトなどの要素が、複数の型に属することができること」を意味しています。

実例で確認してみましょう。

ポリモーフィズムの実例

以下は、ポリモーフィズムを導入したコードの実例です。

//親クラス
class Animal {
    void communicate() {System.out.println("意思疎通する");}
}

//子クラス1
class Dog extends Animal {
    void communicate() {System.out.println("匂いを嗅ぐ");}
}

//子クラス2
class Man extends Animal {
    void communicate() {System.out.println("言葉で話す");}
}

public class Test23 {
    public static void main(String[] args) {
        //インスタンスを生成
        Animal a1 = new Animal();
        Animal d1 = new Dog();
        Animal m1 = new Man();
        //メソッドを呼び出す
        a1.communicate();
        d1.communicate();
        m1.communicate();
    }
}

上記コードには、new Animal()で生成したインスタンスと、new Dog()で生成したインスタンスと、new Man()で生成したインスタンスがあります。

いずれも、生成したインスタンスを同じAnimal型の変数に代入しているため、同じようにcommunicate()メソッドを実行できます。ただし、それぞれのインスタンスでcommunicate()メソッドを実行すると、それぞれの実行結果は以下のように異なります。

意思疎通する
匂いを嗅ぐ
言葉で話す

同じAnimal型の変数に代入されたインスタンスに対して、同じcommunicate()メソッドを実行したにもかかわらず、実行結果が異なります。これがポリモーフィズムです。

親の型に子は入れられる、子の型に親は入れられない

クラスが親子関係にある場合、親クラスの型の変数に対して子クラスのインスタンスも入れることができます。

上記例でいえば、Animal型として定義した変数に親クラスで生成されるAnimalタイプのインスタンスを入られるのはもちろんですが、Animal型として定義した変数には、子クラスで生成されるDogタイプのインスタンスやManタイプのインスタンスも入れられます。

親子を反対にしてDog型にAnimalタイプのインスタンスを入れたり、Man型にAnimalタイプのインスタンスを入れることはできません。つまり、親の型の変数に子インスタンスは入れられますが、子の型の変数に親インスタンスは入れられません。

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

ポリモーフィズムを利用するメリットは、プログラムを柔軟に扱える点にあります。ディテールの異なる複数のインスタンスを1つの型にまとめて放りこんでしまえるので、同じようなコードを何度も書く必要が無くなります。

以下のコードでは、配列に入れた3つのインスタンスをforループで回しながらメソッドを呼び出しています。実行されるcommunicate()メソッドの内容はそれぞれ異なりますが、forループでまとめて回してしまえるので大変便利です。

//親クラス
class Animal {
    void communicate() {System.out.println("意思疎通する");}
}

//子クラス1
class Dog extends Animal {
    void communicate() {System.out.println("匂いを嗅ぐ");}
}

//子クラス2
class Man extends Animal {
    void communicate() {System.out.println("言葉で話す");}
}

public class Test24 {
    public static void main(String[] args) {
    	//Animal型の配列を用意する
    	Animal[] animals = new Animal[3];
        //インスタンスを生成して配列に入れる
    	animals[0] = new Animal();
    	animals[1] = new Dog();
    	animals[2] = new Man();
        //forループで配列を回しながらメソッドを呼び出す
    	for(int i = 0; i < animals.length; i++){
    		animals[i].communicate();
    	}
    }
}

上記コードの実行結果は、以下の通りです。

意思疎通する
匂いを嗅ぐ
言葉で話す

ポリモーフィズムを利用して、将来的な変更に強い柔軟なプログラムに

上記コードのなかには、親クラスとなるAnimalクラス、その子クラスとなるDogクラスとManクラスが存在していますが、将来的にAnimalクラスの子クラスとして、新しくCatクラスを追加する場合を考えてみましょう。

将来的に新しくCatクラスを追加する場合にも、ポリモーフィズムを利用したプログラムにしてあることで、上記コード例の場合にはforループの部分は改変する必要がありません。ポリモーフィズムをうまく利用することで、将来的な変更にも強い柔軟なプログラムを実現できます。

次回へ続きます。