superのコードトレース

superは現在の実体から親クラス側のメンバーやコンストラクタを指定するために使います。thisとの違い、親子の初期化順、隠れたフィールドと上書きされたメソッドの呼出しを整理します。

この単元で学ぶこと

  • superで指定する親メンバーを判断する
  • super(...)による親コンストラクタの初期化順を追う
  • superの利用可能な場所とアクセス制限を判定する

本文解説

初期化の段階を表示する 親フィールド初期化子がP、親コンストラクタがQ、子フィールド初期化子がC、子コンストラクタがDを表示するなら順番はPQCDです。親クラスのstatic初期化等を含めないこの例では、親インスタンス側を完了してから子側へ進みます。

親引数の計算 Child(int n){super(n+1);}ならnew Child(4)は親へ5を渡します。仮引数nをそのまま転送する必要はなく、親呼出しに必要な値を式で計算できます。ただしJava 11で親呼出し前にthisのインスタンス状態を使うことには制約があります。

親の引数なしを明示 Child(){super();...}と省略時の暗黙super()は、親にアクセス可能な引数なしがある通常の例で同じ順序です。明示しても親を二回初期化するわけではありません。

this連鎖と親初期化 Child(){this(3);}Child(int n){super(n);}なら親へ到達するのは委譲先です。親を一回初期化して委譲先の残りを実行し、その後引数なし本体の残りへ戻ります。newが二回行われるわけではありません。

フィールド参照とメソッド呼出し 親型の参照pでp.nを読む場合は宣言型に基づき親フィールドを選びます。一方p.value()のオーバーライド先は実体型から選びます。super.nも親フィールドを明示しますが、普通のメソッド呼出しの動的選択と同じ規則ではありません。

親実装への加算 int value(){return super.value()+3;}なら親の戻り値に子の処理を追加できます。子のvalueを親型変数から呼んでも、まず子の実装へ入り、その中で明示的に親実装を利用します。

親のアクセス可能性 親コンストラクタがprivateなら子からsuper()を呼べません。同じソースファイル内の別トップレベルクラスでも同様です。publicまたは同じパッケージで使えるコンストラクタ等、適切な可視性が必要です。

親フィールド初期化後の子上書き 親のn=2、親本体でn+=3、子本体でsuper.n*=2なら最後は10です。親初期化を終えた値を子側で更新します。子に同名フィールドもある場合は、どちらを更新しているかを明示しないと結果を取り違えます。

図解

親から子への初期化
この図はthis委譲のない通常の親子初期化を示す。

重要ポイント

  • super.nは親側のフィールド、super.m()は親側のメソッド実装を指定する。
  • super(...)は親コンストラクタへの特別な呼出し。
  • Java 11の明示super(...)はコンストラクタの先頭文。
  • superは独立した親オブジェクトを生成する変数ではない。

間違えやすい点

  • superがprivateアクセスを解除すると思わない。
  • this(...)とsuper(...)を同じコンストラクタに二つ書かない。
  • 子のコンストラクタ本文が親より先に実行されると思わない。

用語解説

super
親クラス側のメンバーやコンストラクタを指定するためのキーワード。
フィールドの隠蔽
子の同名フィールドにより単純名が子側を指す状態。
親コンストラクタ呼出し
子の初期化に先立って親側の初期化を行う処理。

関連教材

参考資料

教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。

確認問題の出題・採点や学習履歴の保存にはログインが必要です。