aymnkt
@aymnkt

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

抽象クラスを用いたメソッドの実行方法について

Q&A

Closed

解決したいこと

抽象クラスを用いた場合の、メソッドの実行方法について。

例)
練習用プログラムの例題における、mainクラスからサブクラスのメソッドを実行時の記述において、なぜそうなるのかを知りたいです。

該当するソースコード

public abstract class Player {
	//抽象メソッド
	abstract void play();
}

public class Radio02 extends Player {
	//オーバーライド
	void play() {
		System.out.println("Musicを再生します");
	}
}

public class DvdPlayer02 extends Player {
	void play() {
		System.out.println("Movieを再生します。");
	}
}

public class Polymophism02 {
    public static void main(String[] args) {
		//オブジェクトの生成
		Radio02 myRadio = new Radio02();
		DvdPlayer02 myDvdPlayer = new DvdPlayer02();
		
		//スーパークラスの変数を宣言
		Player myAVPlayer;
		
		//スーパークラスにサブクラスを代入
		myAVPlayer = myRadio;
		myAVPlayer.play();
		
		myAVPlayer = myDvdPlayer;
		myAVPlayer.play();
	}

}

このプログラムにおける以下の部分の、

        //スーパークラスの変数を宣言
		Player myAVPlayer;
		
		//スーパークラスにサブクラスを代入
		myAVPlayer = myRadio;
		myAVPlayer.play();
		
		myAVPlayer = myDvdPlayer;
		myAVPlayer.play();

なぜ上記のように記述する必要があるのでしょうか。
「スーパークラスの変数を宣言」→「スーパークラスにサブクラスを代入」という段階を踏む意味はなんでしょうか。

自分で試したこと

public class Polymophism02 {
    public static void main(String[] args) {
		//オブジェクトの生成
		Radio02 myRadio = new Radio02();
		DvdPlayer02 myDvdPlayer = new DvdPlayer02();
		
		myRadio.play();
        myDvdPlayer.play();
	}

}

と記載しても、実行結果は同じになると思ったのですが、ご教示お願いします。

0

2Answer

下記の例ではスーパークラスを引数に取るplayMediaメソッド作りました。

public class Polymophism02 {
    public static void main(String[] args) {
		Radio02 myRadio = new Radio02();
		DvdPlayer02 myDvdPlayer = new DvdPlayer02();
		
        playMediaPlayer(myRadio);
        playMediaPlayer(myDvdPlayer);
        
        // 例えば新しいPlayerを追加した場合
        // BlurayPlayer myBlurayPlayer = new BlurayPlayer();
        // playMediaPlayer(myBlurayPlayer);
	}

    private static void playMedia(Player player) {
        System.out.println("Playerを再生します。");
        player.play();
    }
}

これのメリットがなにかというと、例えばBlurayPlayerを追加したとしてもplayMediaメソッドを変更する必要がありません。
Playerのサブクラスであればplayメソッドが実装されていることは確実なので、問題なく動作します。

仮にplayRadio(Radio02 radioPlayer)playDvd(DvdPlayer02 dvdPlayer)などのようにサブクラスに限定していた場合はどうでしょうか。
新しくplayBluray(BlurayPlayer blurayPlayer)などのメソッドを作成する必要があったり、あるいはif (player instanceof BlurayPlayer) { ... }のような条件分岐が増える形が予想されます。

身近なものとしてはキーボードをイメージしていただければ良いと思います。
キーの材質や配置などは違っても、Enterキーを押せばパソコンはそれに沿った動作をします。
新商品のキーボードが発売されても、キーボードの仕様を満たしていれば単に接続するだけでよく、パソコン側を変更する必要はありません。
(キーボードの他にはコンセントやUSBポートなども似たようなことが言えます)

このような概念を多態性(ポリモーフィズム)と言います。

3Like

Comments

  1. @aymnkt

    Questioner

    @blue32a
    解説ありがとうございます!多態性について理解が深まりました。

@aymnkt さんが考えた通り、結果としては同じです。

抽象クラスの抽象メソッドを呼び出したが、実際はサブクラスの(オーバーライドした)同名のメソッドが呼び出される、ということを説明したかったのでしょう。

↓こちらも参考にされるとよいと思います。

0Like

Comments

  1. …と記載しても、実行結果は同じになると思ったのですが

    これは、その通りです。
    このサンプルコードでは全く同じ実行結果になります。

    ただ、サンプルコードの範疇としては

    • スーパークラス型の変数に、どのサブクラスのインスタンスでも代入ができる
    • スーパークラスのメソッドとして呼び出すと、実行時には実際のインスタンスのサブクラスのメソッドとして呼び分けられる

    ことの確認が目的と思われるので、このサンプルコードだけを見ても
    「じゃあその呼び分けができて何がうれしいのか」は分かりにくいと思います。

    呼び分けメリットの例

    List<Player> playList = List.of(
      new Radio02(),
      new DvdPlayer02()
    );
    for (Player player : playList) {
      player.play();
    }
    

    たとえば上記のようにプレイリスト的な存在が必要になったと仮定すると
    どのような Player であっても (楽曲でも、映像がある動画でも、オンラインの配信でも)
    とにかく再生(play)が可能ならば、同じように連続で再生する対象として扱うことができます。

    また、一種類の画面UIの操作で、上記のように複数種類の再生可能なモノを扱うこともできます。

    ※例として元のサンプルコードの Player を流用しましたが、こういう場合は実際には「再生可能なアイテム」のような名前になると思います

  2. @aymnkt

    Questioner

    @PECMM
    わかりやすい解説、ありがとうございます!

  3. @aymnkt

    Questioner

    @nak435
    リンクまでご丁寧にありがとうございます!

Your answer might help someone💌