Javaのポリモーフィズムはなぜ使う?同じ呼び出しで処理を切り替える考え方

本記事は、Schooの「Java入門 初級【2026年版】」第7回「継承とインターフェース」を学ぶ前後に、Javaのポリモーフィズムを「なぜ使うのか」から整理したい方向けのステップアップ学習記事です。
結論からいうと、ポリモーフィズムは「種類が増えても、呼び出す側のコードを増やしすぎないため」に使います。現代の開発でも、通知方法、保存先、決済方法、出力形式など、あとから種類が増えやすい処理で重要になります。
Schoo コースページ:Java入門 初級【2026年版】
ポリモーフィズムはなぜ使うのか
たとえば、ユーザーに通知を送る処理を考えます。最初はメール通知だけでも、あとからチャット通知、アプリ通知、ログ出力などが増えるかもしれません。
そのたびに呼び出す側で if 文を増やしていくと、通知方法が増えるほどコードが読みにくくなります。
// 種類が増えるたびに分岐が増えやすい
if (type.equals("email")) {
// メールで通知する
} else if (type.equals("chat")) {
// チャットで通知する
} else if (type.equals("app")) {
// アプリ通知を送る
}
ポリモーフィズムを使うと、呼び出す側は「通知する」という共通の呼び方だけを知っていればよくなります。メールで送るのか、チャットで送るのかは、それぞれのクラスに任せます。
public void notifyUser(NotificationSender sender, String message) {
sender.send(message);
}
呼び出す側は sender.send(message) と書くだけです。実際にメール通知が動くのか、チャット通知が動くのかは、渡されたオブジェクトによって決まります。
同じ呼び出しで違う処理を動かす
ポリモーフィズムとは、同じメソッド呼び出しでも、実際に入っているオブジェクトによって動く処理が変わる仕組みです。
たとえば、NotificationSender というインターフェースに send() があるとします。
public interface NotificationSender {
void send(String message);
}
EmailSender と ChatSender は、どちらも NotificationSender を実装します。
public class EmailSender implements NotificationSender {
@Override
public void send(String message) {
System.out.println("メール送信:" + message);
}
}
public class ChatSender implements NotificationSender {
@Override
public void send(String message) {
System.out.println("チャット送信:" + message);
}
}
どちらのクラスにも send() がありますが、実際の通知方法はそれぞれ違います。この「同じ名前で呼べるが、中身はクラスごとに違う」という状態が、ポリモーフィズムを理解する入口です。
変数の型と実際のオブジェクトを分けて見る
次のコードでは、変数の型は NotificationSender です。しかし、実際に入っているオブジェクトは EmailSender です。
NotificationSender sender = new EmailSender();
sender.send("登録ありがとうございます");
sender.send(...) と書くと、NotificationSender に書かれた形を使って呼び出します。ただし、実際に動くのは EmailSender 側に書いた send() です。
NotificationSenderEmailSender のインスタンスsender.send(...) と書くと、変数の型ではなく、実際に入っているオブジェクトの send() が動きます。
同じsend()でも実行される処理が変わる
次のように、同じ NotificationSender 型の変数に別のオブジェクトを入れることもできます。
NotificationSender email = new EmailSender();
NotificationSender chat = new ChatSender();
email.send("登録ありがとうございます");
chat.send("登録ありがとうございます");
email.send(...) では EmailSender の send() が動きます。chat.send(...) では ChatSender の send() が動きます。
呼び出し方はどちらも send() ですが、実際のオブジェクトが違うため、実行される処理も変わります。
配列にまとめると便利さが見えやすい
ポリモーフィズムの便利さは、複数のオブジェクトを同じ型としてまとめて扱うと見えやすくなります。
NotificationSender[] senders = {
new EmailSender(),
new ChatSender()
};
for (NotificationSender sender : senders) {
sender.send("登録ありがとうございます");
}
配列の中には EmailSender と ChatSender が入っています。それでも、取り出すときは NotificationSender 型として扱い、同じ send() を呼び出せます。
このとき、ループの中でクラス名を判定して処理を分ける必要はありません。各オブジェクトが、自分自身の send() を実行します。
send() を呼ぶだけでよい種類が増えても呼び出す側を変えにくくする
ここまでの通知処理の例で見ると、ポリモーフィズムの実践的な価値は、通知を呼び出す側が具体的な通知方法を知らなくてよい点にあります。
public class NotificationService {
public void send(NotificationSender sender, String message) {
sender.send(message);
}
}
NotificationService は、相手がメール通知なのかチャット通知なのかを知る必要がありません。NotificationSender として受け取り、send() を呼ぶだけです。
そのため、あとから AppSender のような新しい通知方法を追加しても、呼び出す側のコードを大きく変えずに済みます。現代の開発でも、機能追加や仕様変更に合わせてクラスを増やしながら、既存の呼び出し側をできるだけ安定させたい場面でこの考え方が使われます。
確認問題
- ポリモーフィズムを使うと、通知方法が増えたときに呼び出す側のコードをなぜ変えにくくできますか。
NotificationSender sender = new EmailSender();の場合、変数の型と実際のオブジェクトはそれぞれ何ですか。sender.send("登録ありがとうございます");を実行したとき、どのクラスのsend()が動きますか。
演習後の確認:こちらのページを使って、自分で考えた内容を見直せます。
あわせて読みたい関連記事
- Javaの継承とは?親クラスの機能を子クラスで引き継ぐ考え方
- Javaのオーバーライドとは?親クラスのメソッドを子クラスで上書きする考え方
- Javaのインターフェースとは?クラスに共通の使い方を決める仕組み
まとめ
ポリモーフィズムは、同じ呼び出し方で、実際のオブジェクトに応じた処理を動かす考え方です。現代の開発でも、種類が増えても呼び出す側のコードを大きく変えないために使われます。変数の型が NotificationSender でも、中身が EmailSender なら EmailSender の send() が動く、という関係から整理すると理解しやすくなります。