superを理解する

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

この単元で学ぶこと

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

本文解説

親の同名フィールド Parentにint n=2、Childにint n=5がある場合、Childのメソッド内でnやthis.nは子の5、super.nは親の2です。同名でも二つのフィールドが存在します。メソッドのオーバーライドとフィールドの隠蔽は別の仕組みです。

親の実装を呼ぶ Childがname()を上書きしていても、Child内のsuper.name()は親側の実装を選べます。String name(){return super.name()+"C";}なら親の処理へ子の結果を付け足せます。superを使わずname()と呼ぶと自分自身へ再帰する場合があります。

コンストラクタへ値を渡す Parent(int n){this.n=n;}を持つ親に対しChild(){super(7);}と書けば親フィールドを7で初期化できます。親のコンストラクタは子へ継承されないため、子側で適切な初期化経路を定義します。

暗黙のsuper() 子コンストラクタにthis(...)もsuper(...)も書かなければ、通常は引数なしsuper()が先頭に入る形になります。親にアクセス可能な引数なしがないとエラーです。親がint引数しか持たなくても、その引数へ0を自動補完する規則はありません。

親から先に初期化 Parent(){print("P");}Child(){print("C");}でnew Child()するとPCです。親フィールド初期化と親本体の後に、子フィールド初期化と子本体へ進みます。子の本体で親の初期化を後から取り消すわけではありません。

先頭と排他の制約 Java 11では明示的なsuper(...)は子コンストラクタの先頭文です。通常文の後には置けず、同じ本体でthis(...)とsuper(...)を並べることもできません。this(...)を使う場合は委譲した先のコンストラクタ経由で親初期化へ到達します。

アクセス制限は残る 親のprivate nへsuper.nと書いてもアクセスできません。staticメソッドにも対象となる実体がないためsuperを使えません。super.super.nという文法で祖父へ直接さかのぼることもできません。アクセス可能な親のメンバーを通じて処理を定義します。

同じ実体の親部分 super.n=8は現在の子実体が持つ親由来のフィールドを更新します。別のParentオブジェクトを生成したり、すべてのParent実体を更新したりする操作ではありません。thisで見える子側フィールドとsuperで選ぶ親側フィールドを別の欄で追います。

図解

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

重要ポイント

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

間違えやすい点

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

用語解説

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

関連教材

参考資料

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

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