この記事の要点
• 親クラス型の変数で子クラスのインスタンスを受け取れる
• 実際に呼ばれるメソッドは 実行時の型(動的バインディング)で決まる
• `instanceof` で型チェック、Java 16+ は パターンマッチング で簡潔に書ける
このシリーズについて
「Java入門」は1本1テーマで進むシリーズです。本記事は第22回として、ポリモーフィズム(多態性) を扱います。
前提条件: Java入門 #21 - 継承(extends) を読んでいること。クラス・メソッド・継承の基礎知識が必要です。
ポリモーフィズムとは
ポリモーフィズム(polymorphism = 多態性)は、同じ型で扱いながら、実際の動作は中身のクラスによって変わる 仕組みです。
例えば Animal 型の変数に Dog / Cat / Bird のインスタンスを代入でき、makeSound() を呼ぶと、実際に実行されるのは各クラスの実装になります。
// Animal.java(親クラス)
public class Animal {
public void makeSound() {
System.out.println("何か鳴く");
}
}
// Dog.java(子クラス)
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("ワン!");
}
}
// Cat.java(子クラス)
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("ニャー");
}
}
// Bird.java(子クラス)
public class Bird extends Animal {
@Override
public void makeSound() {
System.out.println("チュン");
}
}
// Main.java(Java 21)
public class Main {
public static void main(String[] args) {
Animal[] animals = {
new Dog(),
new Cat(),
new Bird()
};
for (Animal a : animals) {
a.makeSound(); // 実際の型に応じたメソッドが呼ばれる
}
}
}
実行結果:
ワン!
ニャー
チュン
配列の型は Animal[] ですが、中身は Dog / Cat / Bird です。makeSound() を呼ぶと、実際の型(動的型)のメソッド が実行されます。
静的型と動的型
ポリモーフィズムを理解するには、静的型(コンパイル時に決まる型)と動的型(実行時の実際の型)の違いを押さえましょう。
| 用語 | 意味 | 例 |
|---|---|---|
| 静的型(Static Type) | 変数宣言で書いた型。コンパイラが見る | Animal a = ... → 静的型は Animal |
| 動的型(Dynamic Type) | 実行時に代入された実際のインスタンスの型 | ... = new Dog() → 動的型は Dog |
Animal a = new Dog();
// 静的型: Animal
// 動的型: Dog
メソッド呼び出しで実際に実行されるのは 動的型 のメソッドです。これを 動的バインディング(dynamic binding)と呼びます。
ポイント: コンパイラは静的型だけを見て「このメソッドは呼べるか」をチェックします。しかし実行時には動的型のメソッドが呼ばれます。
ポリモーフィズムの利点
なぜ親型でまとめて扱うのでしょうか?主に次の利点があります。
-
配列・コレクションで一括管理できる
Animal[]やList<Animal>に異なる種類の動物を入れて回す -
共通の処理を書ける
「全員makeSound()を呼ぶ」というループを、個別の型を気にせず書ける -
後から新しい子クラスを追加しやすい
Fishクラスを追加しても、既存のAnimal[]に入れるだけで動く
学習のヒント: ポリモーフィズムは「親型で受け取って、中身は自由に差し替える」設計パターンの基礎です。デザインパターン(Strategy, Template Method など)でも頻出します。
instanceof による型チェック
親型で受け取った変数が、実際にどのクラスのインスタンスかを調べたいときは instanceof 演算子を使います。
Animal a = new Dog();
if (a instanceof Dog) {
System.out.println("これは Dog です");
}
| 演算子 | 説明 | 結果 |
|---|---|---|
a instanceof Dog | a が Dog またはそのサブクラスのインスタンスか? | true / false |
a instanceof Animal | a が Animal またはそのサブクラスのインスタンスか? | Dog は Animal を継承するので true |
Java 16+ のパターンマッチング
Java 16 以降では、instanceof で型チェックと同時に キャスト変数を自動で作る ことができます(パターンマッチング)。
// 従来の書き方(Java 15 以前)
if (a instanceof Dog) {
Dog dog = (Dog) a; // 手動キャスト
dog.wagTail(); // Dog 独自のメソッド
}
// Java 16+ のパターンマッチング
if (a instanceof Dog dog) {
dog.wagTail(); // 自動的に Dog 型の変数 dog が使える
}
注意: パターンマッチング変数のスコープは `if` ブロック内のみです。`else` や外側では使えません。
実践例: List で異なる型をまとめる
配列だけでなく、List<Animal> のようなコレクションでもポリモーフィズムが使えます。
import java.util.List;
public class Main {
public static void main(String[] args) {
// Java 21 では List.of() で不変リストを作れる
List<Animal> zoo = List.of(
new Dog(),
new Cat(),
new Bird(),
new Dog()
);
for (Animal animal : zoo) {
animal.makeSound();
// Bird だけ特別な処理
if (animal instanceof Bird bird) {
System.out.println(" → 飛べます");
}
}
}
}
実行結果:
ワン!
ニャー
チュン
→ 飛べます
ワン!
まとめ
| 概念 | 説明 |
|---|---|
| ポリモーフィズム | 親型で受け取り、動的型に応じて動作が変わる仕組み |
| 静的型 | 変数宣言時の型。コンパイラが見る |
| 動的型 | 実行時の実際のインスタンスの型 |
| 動的バインディング | 実行時に動的型のメソッドが呼ばれる仕組み |
| instanceof | 実行時の型をチェックする演算子 |
| パターンマッチング(Java 16+) | instanceof Dog dog で型チェックと同時にキャスト変数を作る |
次のステップ
Java入門 #23 - インターフェース で、クラス階層とは別に「契約」を定義する仕組みを学びます。
参考リソース
- Pattern Matching for instanceof(Oracle) — Java 16+ のパターンマッチング公式ガイド
- Polymorphism(Oracle Java Tutorial) — ポリモーフィズムの基礎
- Java SE 21 ドキュメント