何をするキーワードか
super は「親クラス(スーパークラス)のメンバーを参照する」ためのキーワードです。子クラスでオーバーライドしたメソッドから、親の元の実装を呼びたいときや、親のコンストラクタを明示的に呼びたいときに使います。継承関係で“親の振る舞い・初期化・名前の衝突回避”を扱うための、安全でわかりやすい通路です。
基本の使い方(コンストラクタとメソッド)
親コンストラクタの呼び出し(super(…))
子クラスのコンストラクタでは、最初の行で親のコンストラクタを呼べます。親の初期化が先、子の初期化が後という順序を守るための入り口です。
class Base {
final String name;
Base(String name) {
if (name == null || name.isBlank()) throw new IllegalArgumentException("name");
this.name = name.trim();
}
}
class User extends Base {
final boolean active;
User(String name, boolean active) {
super(name); // 親を先に初期化(必ず最初の行)
this.active = active;
}
}
Java- 重要: super(…) はコンストラクタの先頭行でのみ使用可能。先に自分の処理を書くことはできません。
親メソッドの呼び出し(super.method(…))
オーバーライドしたメソッドで、親の元実装を呼びたい箇所に super を使います。前後に処理を足す、途中で親の検証を挟む、など拡張の定番です。
class Base {
void render() { System.out.println("header"); }
}
class Sub extends Base {
@Override
void render() {
super.render(); // 親の処理を先に実行
System.out.println("body"); // 子の追加
}
}
Java- 使いどころ: 親の共通処理(監査・前処理)を活かしつつ、子で差分だけ追加したいとき。
super が活躍する具体シナリオ(重要ポイントの深掘り)
親の契約を守りながら拡張する
親のメソッドが「必ず行うべき処理」を含むとき、子は super を呼んで契約を守りつつ追加の振る舞いを差し込めます。これにより「壊れやすい基底クラス」問題を避け、拡張の安全性が高まります。
abstract class SecureService {
public void execute(String user) {
audit("start", user);
doExecute(user);
audit("end", user);
}
protected void audit(String phase, String user) { System.out.println("[AUDIT] " + phase + " by " + user); }
protected abstract void doExecute(String user);
}
class TransferService extends SecureService {
@Override
protected void doExecute(String user) {
super.audit("checking", user); // 親の共通機能を再利用
System.out.println("transfer by " + user);
}
}
Java- 重要: 親が提供する“検証・監査・共通前処理”は super で再利用すると、品質のムラが減ります。
名前の衝突回避(フィールド・メソッド)
親と子が同名のフィールドやメソッドを持つ場合、親側を明示的に呼びたいときに super を使います。可読性と意図の明確化に有効です。
class Base {
int count = 1;
void inc() { count++; }
}
class Sub extends Base {
int count = 100; // 子にも同名
@Override void inc() {
super.inc(); // 親のカウントを増やす
count += 10; // 子のカウントも増やす
}
}
Java- 注意: フィールドの「隠蔽」は混乱の元。同名フィールドの定義は避けるのが基本です。どうしても必要なら super で意図を明示。
super と this の違い
- 対象の違い: this は「今のインスタンスの(オーバーライドされた)メソッド/フィールド」。super は「親クラスに定義されたメソッド/フィールド」。
- ディスパッチ: this 経由では動的ディスパッチ(実体の型に応じて切り替え)。super 経由では親の実装が確定で呼ばれます。
- コンストラクタ: this(…) は同一クラスの別コンストラクタ呼び出し、super(…) は親コンストラクタ呼び出し。両方とも先頭行にしか書けないため、同時には使えません。
使えない/誤用しがちなケース(重要な落とし穴)
static 文脈では使えない
static メソッドや static 初期化子の中では super は使えません。super は“インスタンスの親”を指すため、クラス文脈では意味がありません。
class Base { static void util() {} }
class Sub extends Base {
static void util() {
// super.util(); // コンパイルエラー(static では使えない)
}
}
Javaコンストラクタ内でオーバーライド可能メソッドを呼ばない
親のコンストラクタから、子がオーバーライドしたメソッドを呼ぶと、子のフィールドが未初期化のまま参照される危険があります。初期化順序の罠です。
class Base {
Base() { init(); } // NG: コンストラクタ中に呼ぶ
void init() { /* 子が上書きするかもしれない */ }
}
class Sub extends Base {
private String name;
@Override void init() { System.out.println(name.length()); } // name はまだ null
}
Java- 回避策: コンストラクタではオーバーライド可能メソッドを呼ばない。必要なら factory/builder で初期化を段階分けする。
親の契約をバイパスしない
super を呼ばずに親の「必須前処理・検証」を飛ばすと、整合性が崩れます。親が契約を提供しているメソッドは、原則 super を先に呼ぶ設計に。
例題で身につける
例 1: ログ追跡を親に集約、子で差分
class LoggerService {
void process(String task) {
System.out.println("[BEGIN] " + task);
doProcess(task);
System.out.println("[END] " + task);
}
protected void doProcess(String task) { /* default no-op */ }
}
class EmailService extends LoggerService {
@Override protected void doProcess(String task) {
super.process("audit:" + task); // 追加で親の枠組みを再利用(慎重に)
System.out.println("send email: " + task);
}
}
Java- ポイント: 子の追加処理の中で親機能を活用し、監査・ログの統一を保つ。
例 2: 共変戻り値+親処理の拡張
class Animal {}
class Cat extends Animal {}
class Factory {
Animal create() { return new Animal(); }
}
class CatFactory extends Factory {
@Override
Cat create() {
Cat c = new Cat();
// 親の検証メソッドがあるなら super で呼ぶ(例示)
// super.validate(c);
return c;
}
}
Java- ポイント: 戻り値を具体化しつつ、親の共通前処理を維持。
例 3: 名前衝突の明示回避
class Base {
void print() { System.out.println("base"); }
}
class Sub extends Base {
void print() { System.out.println("sub"); } // オーバーロード(引数なし)は“隠蔽”ではなく別メソッドに注意
@Override
void print(String tag) {
super.print(); // 親の出力
System.out.println(tag); // 子の追加
}
}
Java- ポイント: super で親の意図的呼び出しを明確にする。
仕上げのアドバイス(重要部分のまとめ)
super は「親の初期化と元の振る舞いを、安全に再利用するための通路」。コンストラクタでは最初の行で super(…) を呼び、オーバーライドでは super.method(…) で契約と共通処理を守る。static 文脈では使えず、初期化中にオーバーライド可能メソッドを呼ぶのは避ける。親の検証・監査を super で継承して、子は差分だけに集中する——この型を徹底すると、継承は壊れにくく、読みやすく、保守しやすくなります。

