継承の罠
継承の罠
このレッスンでできるようになること。親型の変数に子のインスタンスを入れたとき、フィールドとメソッドがそれぞれどちらの型で解決されるかを言えるようになります。
まずは次のコードの出力を予測してください。
class Parent {
String name = "P";
String who() { return "parent"; }
}
class Child extends Parent {
String name = "C";
String who() { return "child"; }
}
Parent p = new Child();
System.out.println(p.name);
System.out.println(p.who());両方とも子のものが出ると考えた方が多いはずです。しかし実際に出るのは P と child です。片方だけが子になります。
メソッドは実行時の型、フィールドは宣言時の型
インスタンスメソッドの呼び先は、変数の中身の型で決まります。これを動的束縛と呼びます。p の中身は Child なので child が返ります。@Override を書いても書かなくても動きは同じです。
一方フィールドは、変数の宣言に書いた型で決まります。子で親と同じ名前のフィールドを宣言すると、親のフィールドは消えるのではなく隠れているだけです。これをフィールドの隠蔽と呼びます。p の宣言型は Parent なので P が読まれます。
隠れている子のフィールドはキャストすれば読めます。((Child) p).name は C になります。逆に子の中から親のフィールドを読みたいときは super.name と書きます。同じ名前の箱が2つ存在している、という感覚が持てれば十分です。
継承されないもの
試験ではここも狙われます。コンストラクタは継承されません。private なメンバーは子からは見えません。static メソッドは継承されますが、子で同じシグネチャの static メソッドを書いてもオーバーライドにはならず隠蔽になり、やはり宣言型で呼び先が決まります。
演習では、この3つの読み方を1本の文字列にまとめて確かめます。
要件
- 変数の宣言型は
Parentにする - 3つの値をハイフン区切りで連結した文字列を返す
ParentとChildの中身は変更しない
入出力例
predict() → "P-C-child"
predict() → "P-C-child"ヒント
編集 LuaGate編集部