🎯

【Java】クラス解説22・動的メソッドディスパッチ3

2024/01/22に公開

動的メソッドディスパッチ3のコード例

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // アップキャスト
        MyClass obj1 = getName(1);
        MyClass obj2 = getName(2);
        
        // 動的メソッドディスパッチ
        /// ArrayListの型をスーパークラスの型で定義
        ArrayList<MyClass> al = new ArrayList<>();
        /// ArrayListにサブクラスのインスタンスを追加
        al.add(obj1);
        al.add(obj2);
        /// forEachメソッドで、ArrayList内の各インスタンスからprintメソッドを呼び出し
        al.forEach(obj -> obj.print()); //出力結果 -> fuzi, sakura
    }
    
    // 戻り値の型がMyClassで、仮引数iの型がint型のgetNameメソッドを定義
    private static MyClass getName(int i) {
        // 引数に応じて異なるインスタンスを戻り値として返すスイッチ文を使用
        return switch(i) {
            case 1 -> new Fuzi();
            case 2 -> new Sakura();
            default -> new MyClass();
        };
    }
}
// スーパークラスMyClassを定義
class MyClass {
    public void print() {
        System.out.println("MyClass");
    }
}
// サブクラスFuziを定義
class Fuzi extends MyClass{
    // printメソッドをオーバーライド
    public void print() {
        System.out.println("fuzi");
    }
}
// サブクラスSakuraを定義
class Sakura extends MyClass{
    // printメソッドをオーバーライド
    public void print() {
        System.out.println("sakura");
    }
}

コード例の解説

class MyClass {
    public void print() {
        System.out.println("MyClass");
    }
}

スーパークラスのMyClassを定義して、

  1. printメソッドを定義して、
  2. 処理内容は、標準出力にMyClassを出力します。

class Fuzi extends MyClass{
    public void print() {
        System.out.println("fuzi");
    }
}

MyClassを継承したサブクラスFuziを定義して、

  1. スーパークラスのprintメソッドをオーバーライドして、
  2. 処理内容は、標準出力にfuziを出力するよう上書きします。

class Sakura extends MyClass{
    public void print() {
        System.out.println("sakura");
    }
}

MyClassを継承したサブクラスSakuraを定義して、

  1. スーパークラスのprintメソッドをオーバーライドして、
  2. 処理内容は、標準出力にsakuraを出力するよう上書きします。

今回は、Mainクラスを切り抜いて解説します。

MyClass obj1 = getName(1);
MyClass obj2 = getName(2);
private static MyClass getName(int i) {
    return switch(i) {
        case 1 -> new Fuzi();
        case 2 -> new Sakura();
        default -> new MyClass();
    };
}

上記については、アップキャスト2の記事で扱ったものです。

  1. MyClass型の変数obj1に、実引数で数値の1を渡すgetNameメソッドを呼び出し、その戻り値を代入します。
  2. MyClass型の変数obj2に、実引数で数値の2を渡すgetNameメソッドを呼び出し、その戻り値を代入します。
  3. 戻り値の型がMyClassで、仮引数iの型がint型のgetNameメソッドを定義して、
  4. 戻り値としてswitchが使用され、そのswitchの変数iには仮引数の値が代入され、
  5. 1の場合は、Fuzi型のインスタンスが戻り値として返されます。
  6. 2の場合は、Sakura型のインスタンスが戻り値として返されます。
  7. 1または2以外の場合は、デフォルトのMyClass型のインスタンスが戻り値として返されます。

ArrayList<MyClass> al = new ArrayList<>();

al.add(obj1);
al.add(obj2);

al.forEach(obj -> obj.print()); //出力結果 -> fuzi, sakura
  1. 変数alが、MyClassクラスの要素を格納するArrayListであることを宣言しています。
  2. ArrayListである変数alに、obj1(=Fuziクラスのインスタンス)を追加します。
  3. ArrayListである変数alに、obj2(=Sakuraクラスのインスタンス)を追加します。
  4. forEachにより、ArrayListである変数alに格納されている各インスタンスからprintメソッドを呼び出します。その際、動的メソッドディスパッチにより、インスタンスに応じた適切なメソッドが呼び出されます。

まとめ

コレクションフレームワーク(=ArrayList)を利用して、型の異なるインスタンスをまとめて扱っても、適切なメソッドが呼び出される動的メソッドディスパッチが確認されました。

Discussion