default メソッド
メソッドを 1 つ足しただけで、実装クラスが全部壊れる
interface に新しいメソッドを 1 行足すと、それを implements しているクラスがすべてコンパイルエラーになります。契約が増えたのに実装していないからです。社内の 5 クラスなら直せますが、java.util.List のように世界中で実装されている型では現実的ではありません。
それでも Java 8 は List に forEach() を足しました。既存のコードを壊さずに済ませる仕組みを、同時に入れたからです。それが default メソッドです。
default を付けると、既定の中身を一緒に配れる
interface の中で、メソッド宣言の前に default と書き、{ } で本体を書きます。
Java
interface Sensor {
int read();
default String report() {
return "現在値は " + read();
}
}report() は本体を持っているので、以前から Sensor を実装していたクラスは 1 文字も直さなくても そのまま通ります。read() しか書いていないクラスでも、report() が呼べるようになりました。契約は増えたのに、誰も壊れていません。
default の中では、同じ interface の他のメソッドを呼べます。上の例が read() を呼んでいるように、「子が埋めた部分を使って、共通の組み立てだけ親側でやる」書き方ができます。
気に入らないクラスだけが上書きすれば済みます。
Java
class WaterSensor implements Sensor {
@Override
public int read() {
return 18;
}
@Override
public String report() {
return "水温 " + read() + " 度";
}
}上書きしたクラスは自分の実装が、していないクラスは default の実装が動きます。「全員に強制する」ではなく「使いたければ使える既定値を配る」という発想です。
なお default を持つ interface を 2 つ同時に implements して同じ形のメソッドがぶつかると、どちらを使うかを実装クラス側で上書きして決めることになります。
interfaceはフィールドを持てないので、defaultの中で状態を覚えることはできません。引数と、同じinterfaceの他のメソッドから値を組み立てるだけにとどめます。
やってみよう
greetEnglish(String name) を完成させてください。
interface Greeterを定義し、default String greet(String name)を実装する。受け取った名前を"Hello, "に続けて返す形にするGreeterをimplementsするEnglishGreeterクラスを作る。中身は空のままで構いませんgreetEnglishの中でEnglishGreeterのインスタンスを作り、greet(name)の結果を返す
手順 2 で何も書かないのが今回の要点です。空のクラスなのにメソッドが呼べる、という状態を自分の手で確かめてください。"Java" を渡せば "Hello, Java"、空文字なら "Hello, " が返ります。カンマの後ろの半角スペースを忘れないでください。
要件
Solutionの中にinterface Greeterを定義し、default String greet(String name)を実装することGreeterをimplementsするEnglishGreeterクラスを用意すること (中身は空でよい)greetEnglish(String name)の中でEnglishGreeterのインスタンスを生成し、greet(name)の戻り値をreturnすること
入出力例
greetEnglish("Java") → "Hello, Java"
greetEnglish("Alice") → "Hello, Alice"
greetEnglish("") → "Hello, "