オーバーライドを重ねると?
質問:継承を3段以上行ってそれぞれでスーパークラスのメソッドをオーバーライドした場合、どのメソッドが呼ばれますか? たとえばクラスAのサブクラスBがあり、クラスBのサブクラスCがあり、クラスAのprint()メソッドをクラスB・クラスCでオーバーライドしたら、どれが呼ばれますか?
解答:インスタンスを作ったクラスのメソッドが呼ばれます。
継承に継承を重ね、オーバーライドにオーバーライドを重ねた場合、どのメソッドが呼ばれるのか、という点について確認しておきましょう。
今回は3段階に継承して、それぞれのクラスでメソッドをオーバーライドします。
/**
* 普通のクラス。
* メソッド1つ持ちます。
* (HasOneMethodSuperクラスと同じです)
*/
class HasOneMethodSuper3
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "HasOneMethodSuper3" );
}
}
/**
* HasOneMethodSuper3クラスから継承した、サブクラス。
*/
class Sub extends HasOneMethodSuper3
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
System.out.println( "Sub" );
}
}
/**
* Subクラスから継承した、サブクラス。
*/
class SubOfSub extends Sub
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
System.out.println( "SubOfSub" );
}
}
/**
* 実行用クラス。このクラスを実行してください。
*/
class SubOfSubRunner
{
public static void main( String[] args )
{
// SubOfSubクラスのインスタンスを1つ作ります。
SubOfSub ref = new SubOfSub();
// printMyName()メソッドを呼び出します。
ref.printMyName();
// 出力結果:
// SubOfSub
// Subクラスのインスタンスを1つ作ります。
Sub ref2 = new Sub();
// printMyName()メソッドを呼び出します。
ref2.printMyName();
// 出力結果:
// Sub
}
}
// SubOfSubRunner.java /** * 普通のクラス。 * メソッド1つ持ちます。 * (HasOneMethodSuperクラスと同じです) */ class HasOneMethodSuper3 { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "HasOneMethodSuper3" ); } } /** * HasOneMethodSuper3クラスから継承した、サブクラス。 */ class Sub extends HasOneMethodSuper3 { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { System.out.println( "Sub" ); } } /** * Subクラスから継承した、サブクラス。 */ class SubOfSub extends Sub { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { System.out.println( "SubOfSub" ); } } /** * 実行用クラス。このクラスを実行してください。 */ class SubOfSubRunner { public static void main( String[] args ) { // SubOfSubクラスのインスタンスを1つ作ります。 SubOfSub ref = new SubOfSub(); // printMyName()メソッドを呼び出します。 ref.printMyName(); // 出力結果: // SubOfSub // Subクラスのインスタンスを1つ作ります。 Sub ref2 = new Sub(); // printMyName()メソッドを呼び出します。 ref2.printMyName(); // 出力結果: // Sub } }
まず、HasOneMethodSuper3クラスを用意します。
* 普通のクラス。
* メソッド1つ持ちます。
* (HasOneMethodSuperクラスと同じです)
*/
class HasOneMethodSuper3
{
/**
* 自クラスの名前を出力するメソッドです。
*/
void printMyName()
{
System.out.println( "HasOneMethodSuper3" );
}
}
/** * 普通のクラス。 * メソッド1つ持ちます。 * (HasOneMethodSuperクラスと同じです) */ class HasOneMethodSuper3 { /** * 自クラスの名前を出力するメソッドです。 */ void printMyName() { System.out.println( "HasOneMethodSuper3" ); } }
このクラスのprintMyName()メソッドは、自クラスの名前を出力します。
次に、このクラスのサブクラス、Subクラスを作ります。
* HasOneMethodSuper3クラスから継承した、サブクラス。
*/
class Sub extends HasOneMethodSuper3
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
System.out.println( "Sub" );
}
}
/** * HasOneMethodSuper3クラスから継承した、サブクラス。 */ class Sub extends HasOneMethodSuper3 { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { System.out.println( "Sub" ); } }
printMyName()メソッドをオーバーライドしています。
さらに、Subクラスのサブクラス、SubOfSubクラスも作ります。
* Subクラスから継承した、サブクラス。
*/
class SubOfSub extends Sub
{
/**
* 自クラスの名前を出力するメソッドです。
* オーバーライドしています。
*/
void printMyName()
{
System.out.println( "SubOfSub" );
}
}
/** * Subクラスから継承した、サブクラス。 */ class SubOfSub extends Sub { /** * 自クラスの名前を出力するメソッドです。 * オーバーライドしています。 */ void printMyName() { System.out.println( "SubOfSub" ); } }
このクラスでもprintMyName()メソッドをオーバーライドしています。
つまり、printMyName()メソッドは、Subクラス、そしてSubOfSubクラスでもオーバーライドしているわけです。
このSubOfSubクラスのインスタンスを作ってみましょう。
SubOfSub ref = new SubOfSub();
// SubOfSubクラスのインスタンスを1つ作ります。 SubOfSub ref = new SubOfSub();
このSubOfSubクラスのインスタンスにはSubクラスのインスタンスが入っていて、さらにその中にHasOneMethodSuper3クラスのインスタンスが入っています。
この時、HasOneMethodSuper3クラスとSubクラスのprintMyName()メソッドはSubOfSubクラスのprintMyName()メソッドに結びつけられます。
ここでもう1つ例を見てみましょう。
Subクラスのインスタンスを作ってみます。
Sub ref2 = new Sub();
// Subクラスのインスタンスを1つ作ります。 Sub ref2 = new Sub();
このSubクラスのインスタンスの中にはHasOneMethodSuper3クラスのインスタンスが入っています。
この時、printMyName()メソッドはSubクラスのprintMyName()メソッドに結びつけられます。
つまり、どのメソッドが呼び出されるか(どのメソッドにひもが付くか)は、どのクラスのインスタンスを作ったかによる、というわけです。
プログラムだけ見ると、なんだか「どのクラスのインスタンスを作っても、SubOfSubクラスのインスタンスが作られる」ように見えるかもしれません。自分でクラスを作ると、特にそうです。
でも、作られるインスタンスはnewで指定したクラスのインスタンスです。そして、オーバーライドもnewで指定したクラスのメソッドになります。
「どう動くか」をイメージするときには、プログラムそのものではなく、そこから作られるインスタンスをイメージするようにしましょう。そうすれば、どのメソッドが動くのか、イメージしやすいと思います。