前回は、クラスの機能を別のクラスへ引き継ぐ継承について学習しました。
また、親クラスのメソッドを子クラスで書き換えるオーバーライドについても紹介しました。
今回は、この「継承」と「オーバーライド」をさらに活用するポリモーフィズムについて解説します。
ポリモーフィズムとは、簡単にいうと、
「同じメソッドを呼び出しても、実際のオブジェクトによって異なる動作をさせる仕組み」です。
少し難しく感じるかもしれません。
まずは、これまで学んだ継承を思い出してみましょう。
例えば、Carクラスを親クラスとして、SportsCarクラスとTruckクラスを作ってみます。
Car
├── SportsCar
└── Truck
Carには「走る」という共通の機能を持たせます。
public class Car {
public void run() {
System.out.println("車が走ります。");
}
}
SportsCarとTruckは、このCarクラスを継承します。
SportsCarでは、高速で走るようにしてみます。
public class SportsCar extends Car {
@Override
public void run() {
System.out.println("スポーツカーが高速で走ります。");
}
}
Truckでは、大きな荷物を運ぶイメージにしてみましょう。
public class Truck extends Car {
@Override
public void run() {
System.out.println("トラックが荷物を運びながら走ります。");
}
}
どちらもCarクラスのrun()メソッドをオーバーライドしています。
ここからがポリモーフィズムのポイントです。
次のコードを見てみましょう。
Car car1 = new SportsCar();
Car car2 = new Truck();
一見すると少し不思議な書き方です。
Car型の変数に、SportsCarやTruckのオブジェクトを代入しています。
しかし、これは継承関係にあるため可能です。
Car car1 = new SportsCar();
Car car2 = new Truck();
car1.run();
car2.run();
スポーツカーが高速で走ります。
トラックが荷物を運びながら走ります。
ここがポリモーフィズムの重要なポイントです。
どちらもcar1.run()やcar2.run()として同じrun()メソッドを呼び出していますが、実際のオブジェクトに応じて処理が変わっています。
car1とcar2は、どちらも変数の型はCarです。
Car car1 = new SportsCar();
Car car2 = new Truck();
しかし、実際に作られているオブジェクトは異なります。
そのため、run()を呼び出したときには、それぞれのクラスでオーバーライドしたrun()が実行されます。
ポリモーフィズムを理解するためには、
「変数の型」と「実際に作られたオブジェクト」を分けて考えることが大切です。
Car car = new SportsCar();
この場合、
となります。
「Car型だけど、中身はSportsCar」という状態です。
では、わざわざこのような書き方をするメリットは何でしょうか?
例えば、車の種類が増えたとします。
Car
├── SportsCar
├── Truck
└── FamilyCar
それぞれの車にrun()メソッドがあるとします。
ポリモーフィズムを利用すると、親クラスの型としてまとめて扱うことができます。
Car[] cars = {
new SportsCar(),
new Truck(),
new FamilyCar()
};
for (Car car : cars) {
car.run();
}
同じrun()を呼び出しているだけですが、実際のオブジェクトに応じて異なる処理が実行されます。
スポーツカーが高速で走ります。
トラックが荷物を運びながら走ります。
ファミリーカーがゆっくり走ります。
このように、車の種類を1つずつ判定しなくても、それぞれのクラスに応じた処理を実行できます。
難しく考えず、まずは次のように覚えておきましょう。
「同じ呼び出し方で、オブジェクトによって違う動作をさせる」
今回の例なら、
car.run();
という同じ呼び出し方で、
というように動作を変えることができます。
Car car = new SportsCar();
SportsCarがCarを継承しているため可能です。
「SportsCarはCarの一種」と考えると分かりやすいでしょう。
SportsCarがrun()をオーバーライドしている場合、SportsCarのrun()が実行されます。
Car car = new SportsCar();
car.run();
この場合はSportsCarのrun()が実行されます。
ここまでの内容を整理してみましょう。
これらは、それぞれ別々の機能というより、組み合わせて利用することで力を発揮します。
ここまでで、Javaの基本文法からオブジェクト指向まで、一通り学習してきました。
次回からは、ここまで学んだ内容を一度整理してみましょう。
「知っている」から「使える」へ。
これまで学習した文法や考え方を組み合わせながら、Javaの基礎を振り返っていきます。