super で親を呼ぶ
親と同じ名前を書いた瞬間、親の処理が消える
親のメソッドを子で同じ名前にして書き直すと、親の中身は呼ばれなくなります。ところが実際にやりたいのは「全部を置き換える」ではなく「親の結果に一言足す」であることが多いはずです。
しかも、子の中で親と同じ名前をそのまま書くと、呼ばれるのは自分自身です。
Java
class MonthlyReport extends Report {
public String title() {
return "【" + title() + "】"; // 自分を呼び続けて StackOverflowError
}
}この無限再帰を避けて「親のほうの実装」を名指しするキーワードが super です。
super.メソッド名() で親の実装を名指しする
Java
class Report {
public String title() {
return "売上レポート";
}
}
class MonthlyReport extends Report {
@Override
public String title() {
return "【" + super.title() + "】";
}
}super.title() の行で制御が Report 側へ移り、"売上レポート" が返ってきます。それを子の側で加工して返す、という流れです。super は「親としての自分」を指すので、書き方は普通のメソッド呼び出しと変わりません。引数もそのまま渡せます。
さかのぼれるのは 1 段だけです。super.super.title() のように祖父母を直接呼ぶ書き方は Java にはありません。
super(...) はコンストラクタの 1 行目にしか書けない
もう 1 つの使い道が、親のコンストラクタ呼び出しです。
Java
class Report {
protected final String owner;
public Report(String owner) {
this.owner = owner;
}
}
class MonthlyReport extends Report {
private final int month;
public MonthlyReport(String owner, int month) {
super(owner); // ここより上には何も置けない
this.month = month;
}
}super(owner) を書かないと、コンパイラは引数なしの super() を勝手に補おうとします。親に引数なしコンストラクタが無ければ、そこでコンパイルエラーです。前にログを 1 行入れたくなっても、super(...) より上に文は置けません。どうしても前処理したいときは static メソッドに切り出して、super(prepare(owner)) のように引数の中で呼びます。
Android の
onCreateでsuper.onCreate(...)を最初に書くのも同じ理屈です。親の初期化を済ませないうちに自分の処理を始めると、土台の無いところで動くことになります。
やってみよう
describeChild() を完成させてください。
Parentクラスにpublic String greet()を用意し、"Hello"を返すようにするParentを継承したChildを作り、greet()を同じ名前で定義し直すChild側のgreet()では親の実装を呼び、その結果のうしろに" from child"をつなげて返すdescribeChild()はChildのインスタンスを作り、greet()の結果を返す
最終的な戻り値は "Hello from child" です。" from child" の先頭にある半角スペースを落とすと一致しません。Child の中で "Hello from child" を直接返しても値は合ってしまいますが、それでは親を呼んでいないので、必ず親の実装を経由してください。
要件
ChildクラスはParentをextendsで継承することChild.greet()はsuper.greet()を呼び、その結果に" from child"を連結して返すことSolution.describeChild()はnew Child().greet()の結果をreturnし、最終的に"Hello from child"を返すこと
入出力例
describeChild() → "Hello from child"