Udemyの人気コースが今なら80%OFF!

【Java入門】ポリモーフィズム

【Java入門】ポリモーフィズム

ポリモーフィズム

ポリモーフィズムを説明するために、サイヤ人クラスとそれを継承した悟空クラス、ベジータクラスを作ります。

Saiyan.java(サイヤ人クラス)

//サイヤ人クラス
public abstract class Saiyan {

    public void attack(){
        System.out.println("サイヤ人の攻撃!");
    }

    public void skill(){
        System.out.println("必殺技!");
    }

    public void destroyTheStar(){
        System.out.println("星を破壊する!");
    }
}

Goku.java(悟空クラス)

//悟空クラス
public class Goku extends Saiyan{

    public void attack(){
        System.out.println("悟空の攻撃!");
    }

    public void skill(){
        System.out.println("かめはめ波!");
    }

    public void work(){
        System.out.println("悟空は働いた!");
    }
}

Vegeta.java(ベジータクラス)

//ベジータクラス
public class Vegeta extends Saiyan{

    public void attack(){
        System.out.println("ベジータの攻撃!");
    }

    public void skill(){
        System.out.println("ギャリック砲!");
    }

    public void work(){
        System.out.println("ベジータは働いた!");
    }
}

悟空とベジータの必殺技を繰り出す。

最初に定義したクラスを基に悟空インスタンスとベジータインスタンスを生成して、skillメソッドを実行してみます。

Java

//悟空とベジータをインスタンス化。
Saiyan s1 = new Goku();
Saiyan s2 = new Vegeta();

//悟空
s1.skill();

//ベジータ
s2.skill();

実行結果

かめはめ波!
ギャリック砲!
解説
悟空の必殺技である「かめはめ波」とベジータの必殺技である「ギャリック砲」を出すことができました。
これがポリモーフィズムです。
ポリモーフィズムとは
継承関係にある親子クラスの場合、親クラスの型に子クラスのインスタンスを代入できることです。

サイヤ人は働かない?

では、悟空クラスとベジータクラスが持つworkメソッドを実行するとどうなるのかを見ていきましょう。

Java

//悟空とベジータをインスタンス化。
Saiyan s1 = new Goku();
Saiyan s2 = new Vegeta();

//悟空
s1.work();//ここでコンパイルエラー。
解説
このプログラムの開発者は、サイヤ人は全員無職で働かない人種だと思っているので、サイヤ人クラスにはworkメソッドを定義していません。
つまり、悟空やベジータが働くことを想定していないのです。
呼び出せるメソッドは、左辺のクラスで定義しているメソッドのみということです。
悟空とベジータが働けるようにするためには、以下のように記述する必要があります。

Java

        //悟空とベジータをインスタンス化。
        Saiyan s1 = new Goku();
        Saiyan s2 = new Vegeta();

        //悟空
        Goku g = (Goku)s1;
        g.work();

        //ベジータ
        Vegeta v = (Vegeta)s2;
        v.work();

実行結果

悟空は働いた!
ベジータは働いた!
解説
悟空とベジータが働けるようにするためには、サイヤ人クラスとして作っていたs1、s2オブジェクトをGokuクラスやベジータクラスのオブジェクトとして使えるようにキャストする必要があります。(6行目、10行目)

悟空とベジータは別人

悟空インスタンスが入ったサイヤ人オブジェクトs1を間違えてベジータクラスでキャストしてしまうとClassCastExceptionが発生します。

Java

//悟空とベジータをインスタンス化。
Saiyan s1 = new Goku();
Saiyan s2 = new Vegeta();

//悟空
Vegeta g = (Vegeta)s1;
g.work();

実行結果

Exception in thread "main" java.lang.ClassCastException: test.Goku cannot be cast to test.Vegeta
    at test.Main.main(Main.java:6)
解説
悟空とベジータは別人なので、悟空インスタンスをベジータクラスのオブジェクトにキャストすることは出来ません。
ソースのコンパイルは成功しますが、実行時にClassCastExceptionが発生してしまいます。
この実行時エラーを防ぐにはinstanceofを使ってキャスト可能かどうかをチェックする必要があります。

Java

//悟空とベジータをインスタンス化。
Saiyan s1 = new Goku();
Saiyan s2 = new Vegeta();

//悟空がベジータとして使えるかをチェックする。
if(s1 instanceof Vegeta ){
    Vegeta g = (Vegeta)s1;
    g.work();
}else{
    System.out.println("悟空はベジータには成れません。");
}

実行結果

悟空はベジータには成れません。
解説
instanceofでチェックすることによりClassCastExceptionを防ぐことが出来ます。

サイヤ人は星を破壊する悪いヤツ?

Java

//悟空とベジータをインスタンス化。
Saiyan s1 = new Goku();
Saiyan s2 = new Vegeta();

//悟空
s1.destroyTheStar();

//ベジータ
s2.destroyTheStar();

実行結果

星を破壊する!
星を破壊する!
解説
悟空の実行結果もベジータの実行結果も同じになってしまいました。
これはどういうことかというと、このプログラムの開発者は「サイヤ人は星を破壊する悪いヤツ」だと思っているので、サイヤ人クラスに`destroyTheStar`メソッドを定義していたんですね。
一方、悟空とベジータは地球に来て長いのでキレイな心を手に入れてそんな野蛮なことは考えなくなった。と、このプログラムの開発者は考えたので、悟空クラスとベジータクラスには`destroyTheStar`メソッドを定義しませんでした。
その結果、サイヤ人の`destroyTheStar`メソッドが実行され、悟空もベジータも「星を破壊する!」となってしまったんですね~。

まとめ

ポリモーフィズムで大事なことは以下の3つです。

  1. 代入できるのは継承関係にある親子クラス。
  2. 左辺のクラスで定義しているメソッドしか実行できない。
  3. 右辺のクラスでメソッドをオーバーライドしていれば、右辺のメソッドを実行できる。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です