この章では、オブジェクト指向で一番「面白い」であろう「ポリモーフィズム」というものを紹介します。
かなり突拍子もない不思議な機能なのですが、オブジェクト指向では必須の機能です。最初は戸惑うかもしれませんが、恐れず挑戦してみましょう。
「参照型変数」の型を変える
これまでずっと「参照型変数の型は、インスタンスの型と同じにする」のだと説明してきました。
たとえば、「5.2 継承で機能を加える」で紹介したAddedSubクラスを例に、インスタンスを作成している箇所を見てみると、両方とも同じ型になっています。
AddedSub ref = new AddedSub();
「参照型変数refの型」と「newで指定する型」が両方ともAddedSubになっています。
「AddedSubクラスのインスタンス」に結びついている参照の型は「AddedSubクラスの参照型」となるので、それを「AddedSubクラスの参照型変数に入れる」のは当然と言えます。
ところが実は、参照型変数は、インスタンス作成時の型だけではなく、そのスーパークラスの型で作っても、参照を受け取ることができます。
たとえば上記の例では、ref変数は「AddedSubクラス」だけでなく、「NormalSuperクラス」で作ってもいいんです。
というわけで、例によってサンプルプログラムで確認してみましょう。
/**
* 普通のクラス。
* (NormalSuperクラスと全く同じクラスです)
*/
class NormalSuper2
{
// int型変数のdataフィールドです。
int data;
/**
* フィールドを出力するメソッドです。
*/
void printData()
{
System.out.println( data );
}
}
/**
* NormalSuper2クラスから継承した、サブクラス。
* フィールドとメソッドを追加しています。
* (AddedSubクラスと全く同じクラスです)
*/
class AddedSub2 extends NormalSuper2
{
// int型変数のdataSubフィールドです。
int dataSub;
/**
* フィールドを出力するメソッドです。
*/
void printDataSub()
{
// このクラスで追加したフィールドを出力。
System.out.println( dataSub );
// ついでにスーパークラスのフィールドも出力。
System.out.println( data );
}
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class AddedSub2Runner
{
public static void main( String[] args )
{
// インスタンスを1つ作り数値を入れます。
// ただし、参照型変数はスーパークラスの型にします。
NormalSuper2 ref = new AddedSub2();
// スーパークラスのフィールドに値を入れて出力します。
ref.data = 100;
ref.printData();
// 出力結果:
// 100
}
}
// AddedSub2Runner.java /** * 普通のクラス。 * (NormalSuperクラスと全く同じクラスです) */ class NormalSuper2 { // int型変数のdataフィールドです。 int data; /** * フィールドを出力するメソッドです。 */ void printData() { System.out.println( data ); } } /** * NormalSuper2クラスから継承した、サブクラス。 * フィールドとメソッドを追加しています。 * (AddedSubクラスと全く同じクラスです) */ class AddedSub2 extends NormalSuper2 { // int型変数のdataSubフィールドです。 int dataSub; /** * フィールドを出力するメソッドです。 */ void printDataSub() { // このクラスで追加したフィールドを出力。 System.out.println( dataSub ); // ついでにスーパークラスのフィールドも出力。 System.out.println( data ); } } /** * 実行用クラス。このクラスを実行してください。 */ class AddedSub2Runner { public static void main( String[] args ) { // インスタンスを1つ作り数値を入れます。 // ただし、参照型変数はスーパークラスの型にします。 NormalSuper2 ref = new AddedSub2(); // スーパークラスのフィールドに値を入れて出力します。 ref.data = 100; ref.printData(); // 出力結果: // 100 } }
NormalSuper2クラスとAddedSub2クラスは「5.2 継承で機能を加える」とほぼ同じクラスです。クラス名が違うだけで、フィールド・メソッドは全く同じです。
継承関係も同じです。NormalSuper2クラスがスーパークラス、AddedSub2クラスがサブクラスになります。
さて、これまでの例では、AddedSub2クラスのインスタンスに結びつく参照を、AddedSub2クラスの参照型変数に入れていました。
でも、今回はAddedSub2クラスの参照型変数ではなく、そのスーパークラスの、NormalSuper2クラスの参照型変数に入れています。
// ただし、参照型変数はスーパークラスの型にします。
NormalSuper2 ref = new AddedSub2();
// インスタンスを1つ作り数値を入れます。 // ただし、参照型変数はスーパークラスの型にします。 NormalSuper2 ref = new AddedSub2();
ref変数は、AddedSub2クラスの参照型ではなく、NormalSuper2クラスの参照型です。
つまり、インスタンスの型ではなく、そのスーパークラスの型ということです。
このような、「スーパークラスの参照型へ、サブクラスの参照を入れる」ことを「アップキャスト」と言います。
「AddedSub2クラスの参照型」から「NormalSuper2クラスの参照型」へ「型変換」されるので、キャストの一種になります。ただし、intからlongへのキャストが何もしなくてもできるように、アップキャストも「特になにもしなくても自動的に行われる型変換」です。
このアップキャストだけ見ると「なんのためにこんな機能が?」と思われるかもしれません。
でもこの機能が「ポリモーフィズム」という機能に大きく関わってくるのです。