この章では、継承についてわかりにくい部分、謎な箇所について説明していきます。
スーパークラスからサブクラスは?
質問:オーバーライドすると、スーパークラスにある「された側のメソッド」はなくなっちゃうの?
解答:なくなりません。ちゃんと残っていますし、使うこともできます。
オーバーライドすると、サブクラスでオーバーライドしたメソッドが呼ばれるようになるため、スーパークラスにある「オーバーライドされたメソッド」はなくなってしまうように思うかもしれません。
でもそんなことはありません。オーバーライドされたメソッドもちゃんと残っていますし、使えます。
使い方は、「superフィールド」というフィールドを使うだけです。
/**
* 普通のクラス。
* メソッド1つ持ちます。
* (HasOneMethodSuperクラスと同じです)
*/
class HasOneMethodSuper2
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "HasOneMethodSuper2" );
}
}
/**
* HasOneMethodSuper2クラスから継承した、サブクラス。
* メソッドを追加しています。
*/
class UseSuperSub extends HasOneMethodSuper2
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
// スーパークラスのメソッドを呼び出します。
super.printMyName();
}
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class UseSuperSubRunner
{
public static void main( String[] args )
{
// インスタンスを1つ作ります。
UseSuperSub ref = new UseSuperSub();
// printMyName()メソッドを呼び出します。
ref.printMyName();
// 出力結果:
// HasOneMethodSuper2
}
}
// UseSuperSubRunner.java /** * 普通のクラス。 * メソッド1つ持ちます。 * (HasOneMethodSuperクラスと同じです) */ class HasOneMethodSuper2 { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "HasOneMethodSuper2" ); } } /** * HasOneMethodSuper2クラスから継承した、サブクラス。 * メソッドを追加しています。 */ class UseSuperSub extends HasOneMethodSuper2 { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { // スーパークラスのメソッドを呼び出します。 super.printMyName(); } } /** * 実行用クラス。このクラスを実行してください。 */ class UseSuperSubRunner { public static void main( String[] args ) { // インスタンスを1つ作ります。 UseSuperSub ref = new UseSuperSub(); // printMyName()メソッドを呼び出します。 ref.printMyName(); // 出力結果: // HasOneMethodSuper2 } }
まず、HasOneMethodSuper2クラスを用意します。
* 普通のクラス。
* メソッド1つ持ちます。
* (HasOneMethodSuperクラスと同じです)
*/
class HasOneMethodSuper2
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "HasOneMethodSuper2" );
}
}
/** * 普通のクラス。 * メソッド1つ持ちます。 * (HasOneMethodSuperクラスと同じです) */ class HasOneMethodSuper2 { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "HasOneMethodSuper2" ); } }
このクラスのprintMyName()メソッドは、自クラスの名前を出力します。
さて、このprintMyName()メソッドをオーバーライドしたいんだけれども、でもその中でオーバーライドされたメソッドを呼びたい(つまりHasOneMethodSuper2クラスのprintMyName()メソッドを呼びたい)という場合どうすればいいのかというと、次のUseSuperSubクラスのように「superフィールド」を使います。
* HasOneMethodSuper2クラスから継承した、サブクラス。
* メソッドを追加しています。
*/
class UseSuperSub extends HasOneMethodSuper2
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
// スーパークラスのメソッドを呼び出します。
super.printMyName();
}
}
/** * HasOneMethodSuper2クラスから継承した、サブクラス。 * メソッドを追加しています。 */ class UseSuperSub extends HasOneMethodSuper2 { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { // スーパークラスのメソッドを呼び出します。 super.printMyName(); } }
このUseSuperSubクラスはHasOneMethodSuper2クラスのサブクラスで、printMyName()メソッドをオーバーライドしています。
このメソッドの中で「super」というフィールドを使っています。
これは、何もしなくても勝手に作られるフィールドです。自動的に作られるという点では「3.3 thisフィールドで自分を見る」で紹介したthisフィールドと同じですね。
ではsuperフィールドはどういうフィールドなのかというと、「スーパークラスのインスタンスを指す参照型変数」です。
superフィールドは参照型変数で、スーパークラスのインスタンスに結びついた参照を持っています。そのため、superフィールドを使うことで「オーバーライドされたメソッドを使うことができる」というわけです。
インスタンスを作ると、このsuperフィールドが自動的に作られ、スーパークラスのインスタンスへの参照が入れられます。
UseSuperSub ref = new UseSuperSub();
// インスタンスを1つ作ります。 UseSuperSub ref = new UseSuperSub();
サブクラスのインスタンスを作ることで、その中にスーパークラスのインスタンスが作られる、というのはこれまでの説明通りです。
実はこの時、自動的に「super」という名前のフィールドが作られ、その中に「スーパークラスのインスタンス」への参照が入れられます。
このsuperフィールドはthisフィールドと同じように使うことができます。
そして、このsuperフィールドを通してメソッドを呼び出すと、オーバーライドされたメソッド、つまり「上書きされたはずのメソッド」を呼び出すことができます。
superを通して呼び出すことで、「サブクラスでオーバーライドしたメソッド」から「スーパークラスにあるオーバーライドされたメソッド」を呼び出すことができる、というわけです。
このことから、オーバーライドされたメソッドが決して消えたわけではないことが分かると思います。
オーバーライドしても「完全に塗りつぶして上書きする」わけではありません。オーバーライドしたメソッドを呼ぶようにしただけで、結果、上書きした形になるだけです。
メソッドに機能を追加する
今回の例では、オーバーライドされたメソッドをただ呼び出すだけなのであまりメリットはありません。
この機能は、メソッドに機能を追加する点にメリットがあります。
オーバーライドされたメソッドを呼びだす箇所の前後に処理を追加すると、「既存のメソッドに処理を追加する」ことになります。
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
// ここに前処理を追加できます。
// スーパークラスのメソッドを呼び出します。
super.printMyName();
// ここに後処理を追加できます。
}
/** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { // ここに前処理を追加できます。 // スーパークラスのメソッドを呼び出します。 super.printMyName(); // ここに後処理を追加できます。 }
「super.printMyName();」が「既存のメソッド」になります。
その前後に処理を追加することで、「既存のメソッドに機能追加をする」ことになります。
このように、オーバーライドは単に「メソッドを上書きする」だけではなく、「メソッドに機能を追加する」という使い方もできるわけです。この機能をうまく使うことで、プログラムの再利用がよりしやすくなるでしょう。