ポリモーフィズム(多態性)とは
ポリモーフィズムは、オブジェクト指向プログラミングの核心概念の一つであり、同じインタフェースや基底クラスを通じて、異なるサブクラスの振る舞いを柔軟に扱うことを可能にします。つまり、「一つの型で複数の形を表現できる」仕組みです。
この特性により、コードの再利用性が高まり、拡張性のある設計が実現できます。基本的な実現方法はスーパークラスの参照変数にサブクラスのインスタンスを代入することです。
基本構文と動作原理
たとえば、Animal クラスを親クラスとし、Person クラスがそれを継承している場合:
Animal a = new Person();
このとき、変数 a は Animal 型として宣言されていますが、実際には Person のインスタンスを指しています。このような関係を「アップキャスト」と呼びます。
メソッドとフィールドの解決ルール
- メソッド呼び出し:オーバーライドされたメソッドは、実行時のオブジェクト型に基づいて動的に解決されます(動的ディスパッチ)。したがって、
a.setAge()を呼び出すと、PersonクラスのsetAge()が実行されます。 - フィールドアクセス:フィールドは静的に解決されるため、
a.ageは常にAnimalクラスに定義されたageを参照します。サブクラスの同名フィールドは隠蔽されますが、オーバーライドとはみなされません。
サブクラス固有のメンバーにアクセスするには
スーパークラスの参照ではサブクラス独自のメソッド(例: getName())に直接アクセスできません。これを可能にするには、明示的なダウンキャストが必要です。
if (a instanceof Person) {
Person p = (Person) a;
p.getName();
}
instanceof を用いることで、キャストが安全であるか事前にチェックでき、ClassCastException を防ぐことができます。
サンプルコードによる検証
public class Animal {
int age = 1;
public void setAge() {
age = 10;
System.out.println("AnimalクラスのsetAgeメソッド");
}
}
public class Person extends Animal {
int age = 2;
@Override
public void setAge() {
age = 20;
System.out.println("PersonクラスのsetAgeメソッド");
}
public void getAge() {
System.out.println("PersonクラスのgetAgeメソッド: age = " + age);
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Person();
System.out.println(a.age); // 結果: 1(Animalのフィールド)
a.setAge(); // 結果: "PersonクラスのsetAgeメソッド"
if (a instanceof Person) {
Person p = (Person) a;
p.getAge(); // 結果: "PersonクラスのgetAgeメソッド: age = 20"
}
}
}
実際の応用例:銀行システムでの多態性活用
抽象クラスを活用することで、異なる顧客タイプを統一的に処理できます。
public abstract class Customer {
protected String name;
public abstract void come();
}
public class VIPCustomer extends Customer {
public VIPCustomer(String name) {
this.name = name;
}
@Override
public void come() {
System.out.println("VIP顧客 " + name + " が業務を開始しました。");
}
}
public class CommenCustomer extends Customer {
public CommenCustomer(String name) {
this.name = name;
}
@Override
public void come() {
System.out.println("一般顧客 " + name + " が業務を開始しました。");
}
}
public class Bank {
public void handle(Customer c) {
c.come();
}
public static void main(String[] args) {
Bank bank = new Bank();
Customer v = new VIPCustomer("田中宏");
Customer c = new CommenCustomer("佐藤美咲");
bank.handle(v); // 出力: VIP顧客 田中宏 が業務を開始しました。
bank.handle(c); // 出力: 一般顧客 佐藤美咲 が業務を開始しました。
}
}
このように、handle(Customer) メソッドは引数の実際の型に関わらず共通のインターフェースで処理でき、新しい顧客タイプが追加されてもメソッド自体を変更する必要がありません。