Menu
Coddy logo textTech

メソッドオーバーライド(実行時)

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 29/87。

メソッドのオーバーロードはコンパイル時に解決されますが、メソッドのオーバーライド実行時ポリモーフィズムを示します。ここでは、Javaはプログラムが実際に実行されているときに、変数の宣言された型ではなく、オブジェクトの実際の型に基づいて、どのメソッドを実行するかを決定します。

継承の章でメソッドのオーバーライドについて学びました。これがポリモーフィック(多態的)である理由は、親クラスの参照が子クラスのオブジェクトを指しているときに、Javaがそれをどのように処理するかにあります。

public class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Bark!");
    }
}

public class Cat extends Animal {
    @Override
    public void speak() {
        System.out.println("Meow!");
    }
}

親型の参照を使用するとどうなるか見てみましょう:

Animal myPet = new Dog();
myPet.speak();  // 出力: Bark!

myPet = new Cat();
myPet.speak();  // 出力: Meow!

myPetAnimal として宣言されていても、Javaは実行時に実際のオブジェクト型に基づいてオーバーライドされたメソッドを呼び出します。これは 動的メソッドディスパッチ と呼ばれます。JVMは変数が何であるかではなく、オブジェクトが実際に何であるかを確認します。

これにより、異なるオブジェクトを均一に処理するような強力なパターンが可能になります:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // それぞれが自身のバージョンを呼び出します
}

実行時ポリモーフィズムを使用すると、親型を扱いながら、自動的に正しい子の振る舞いを使用する柔軟なコードを記述できます。

challenge icon

チャレンジ

簡単

実行時ポリモーフィズム(多態性)の動作を実証する通知システムを構築しましょう。通知タイプの継承階層を作成し、実行時の実際のオブジェクトタイプに応じて、同じメソッド呼び出しが異なる出力を生成するようにします。

コードは4つのファイルに分けて構成します:

  • Notification.java: すべての通知が共有するベースクラスを作成します。すべての通知は recipient フィールド(String)を持ちます。これを初期化するためのコンストラクタ、ゲッターメソッド、および "Sending notification to " + recipient と出力する send() メソッドを含めてください。
  • EmailNotification.java: Notification を継承するクラスを作成します。メール通知には subject フィールドを追加します。親クラスの初期化には super を使用してください。send() メソッドをオーバーライドして、"Emailing " + getRecipient() + ": " + subject と出力するようにします。
  • SMSNotification.java: Notification を継承する別のクラスを作成します。SMS通知には phoneNumber フィールドを追加します。send() をオーバーライドして、"Texting " + phoneNumber + " for " + getRecipient() と出力するようにします。
  • Main.java: ここで実行時ポリモーフィズムが真価を発揮します!受信者名、メールの件名、電話番号の3つの入力を受け取ります。Notification 参照の配列を作成し、次の順序で3つのオブジェクトを格納します:ベースの Notification、EmailNotification、および SMSNotification(すべて同じ受信者を使用します)。次に、配列をループで回し、各要素に対して send() を呼び出します。Javaが各オブジェクトの実際の内容に基づいて、正しいオーバーライドされたバージョンを自動的に呼び出す様子を確認してください!

入力として、受信者名(String)、メールの件名(String)、電話番号(String)の3つを受け取ります。

出力には3行表示されるはずです。Notification 参照に対して同じメソッドを呼び出しているにもかかわらず、各 send() 呼び出しは異なる出力を生成します。これが動的メソッドディスパッチの仕組みです!

チートシート

メソッドのオーバーライドは、実行時ポリモーフィズム(多態性)を示しています。これは、Javaがプログラムの実行時に、変数の宣言された型ではなく、オブジェクトの実際の型に基づいて、どのメソッドを実行するかを決定する仕組みです。

親クラスの参照が子クラスのオブジェクトを指している場合、Javaは実行時に実際のオブジェクト型に基づいてオーバーライドされたメソッドを呼び出します:

public class Animal {
    public void speak() {
        System.out.println("Some sound");
    }
}

public class Dog extends Animal {
    @Override
    public void speak() {
        System.out.println("Bark!");
    }
}

Animal myPet = new Dog();
myPet.speak();  // 出力: Bark!

myPet = new Cat();
myPet.speak();  // 出力: Meow!

これは動的メソッドディスパッチと呼ばれます。JVMは変数の型ではなく、オブジェクトが実際に何であるかを確認します。

実行時ポリモーフィズムにより、異なるオブジェクトを統一的に処理することが可能になります:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // それぞれが自身のバージョンを呼び出す
}

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み込む
        String recipient = scanner.nextLine();
        String subject = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        
        // TODO: 3つの要素を持つ Notification 参照の配列を作成する
        // 配列には(次の順序で)含める必要があります:
        // 1. 基本の Notification オブジェクト
        // 2. EmailNotification オブジェクト
        // 3. SMSNotification オブジェクト
        // すべて同じ recipient を使用すること
        
        // TODO: 配列をループで回し、各要素に対して send() を呼び出す
        // これは実行時ポリモーフィズムを示しています!
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン