default メソッド
default メソッド とは
Java 8 で導入された interface の default メソッドの使い方と、後方互換性を保ちながら共通実装を提供する仕組みを学び、英語版あいさつメソッドを書いてみよう。
default メソッドとは何か
interface (インターフェース) は、それまでの Java では「抽象メソッドの宣言だけを並べる場所」でした。abstract キーワードがなくても自動的に public abstract になるので、実装を書けるのは class 側だけ、というのが鉄則だったのです。ところが Java 8 から、その鉄則を破る default メソッドという仕組みが追加されました。default を付ければ、interface の中にも本体付きのメソッドを定義できるようになります。
なぜ後から仕様を変えたのか、と疑問に思った人は鋭いです。きっかけは
java.util.Collectionやjava.util.Listにstream()forEach()といったメソッドを追加したかったから、というのが大きな理由です。古いinterfaceにメソッドを足すと、既存の実装クラスが全部コンパイルエラーになってしまうので、defaultで「未実装でも動く既定の振る舞い」を提供できるようにしたわけです。
このレッスンでは、default メソッドの目的と書き方を押さえ、Greeter という小さな interface に英語版あいさつを実装してみます。function_name は greetEnglish で、英語の Greeter 実装の greet("Java") の結果として "Hello, Java" を返す、というのがゴールです。
default の書き方
書き方はとてもシンプルで、interface の中でメソッド宣言の前に default キーワードを付け、{} で本体を書くだけです。public は省略しても自動で public 扱いになります。
Java
public interface Greeter {
default String greet(String name) {
return "Hello, " + name;
}
}この interface を implements したクラスは、greet を書かなくても default の実装をそのまま使えます。たとえば EnglishGreeter を作って何もメソッドを書かなくても、new EnglishGreeter().greet("Java") は "Hello, Java" を返します。
Java
public class EnglishGreeter implements Greeter {
// greet を書かなくても default が使われる
}
defaultメソッドの本体ではthisが使えますし、同じinterface内の他のdefaultメソッドやabstractメソッドも呼べます。ただしinterfaceにはフィールド(インスタンス変数) を持てないので、状態を持つロジックは書けません。あくまで「引数と他のメソッドから値を組み立てる」純粋関数的な実装に留めるのが基本です。
必要なら実装クラス側で override して上書きもできます。@Override アノテーションを付けると、書き間違いをコンパイラが検出してくれるので付けるのがおすすめです。JapaneseGreeter で「こんにちは, Java」を返すようにしたいときはこう書きます。
Java
public class JapaneseGreeter implements Greeter {
@Override
public String greet(String name) {
return "こんにちは, " + name;
}
}abstract class との違い
「本体付きメソッドを持てる」と聞くと、abstract class (抽象クラス) と何が違うのか気になるはずです。一見似ていますが、根本的に違う点が 3 つあります。
interfaceはフィールドを持てない (定数public static finalだけは持てる)。abstract classはインスタンス変数を自由に持てる- クラスは
extendsで 1 つの親クラスしか継承できないが、implementsは複数のinterfaceを同時に実装できる interfaceのメソッドはデフォルトでpublic。abstract classはprotectedや package-private を自由に使える
つまり「状態」を持ちたいなら abstract class、「振る舞いの契約」だけを多重に組み合わせたいなら interface + default、という使い分けになります。Comparable Iterable AutoCloseable のような Java の標準 API も、振る舞いの契約として interface を使っているのを思い出してください。
多重継承の衝突解決
default メソッドの強力さと引き換えに、多重継承 の問題が再びやってきます。2 つの interface が同じシグネチャの default メソッドを持っていて、それを同時に implements するとどうなるか、というやつです。
Java
public interface Greeter {
default String greet(String name) { return "Hello, " + name; }
}
public interface Farewell {
default String greet(String name) { return "Bye, " + name; }
}
public class Bot implements Greeter, Farewell {
// コンパイルエラー: greet の実装が衝突
}この場合、Java は「どちらを使えばいいか分からない」とエラーにします。解決方法は 実装クラス側で greet を override して明示する ことです。元の default を呼びたいときは Greeter.super.greet(name) のように interface 名.super.メソッド名(...) の構文を使います。
Java
public class Bot implements Greeter, Farewell {
@Override
public String greet(String name) {
return Greeter.super.greet(name);
}
}解決順序のルールは「クラス優先 → 親クラス優先 → 具体的な
interface優先」です。クラス階層に同じシグネチャがあれば、そちらが必ず勝ちます。interfaceのdefaultは、あくまでクラスが提供しないときの「フォールバック実装」と覚えるとイメージしやすいです。
interface の static メソッド
default と並んで、Java 8 から interface の中に static メソッドも書けるようになりました。static メソッドは interface 名から直接呼ぶユーティリティで、実装クラスからは継承されません。
Java
public interface Greeter {
default String greet(String name) {
return "Hello, " + name;
}
static Greeter english() {
return new EnglishGreeter();
}
}
// 呼び出し側
Greeter g = Greeter.english();List.of(1, 2, 3) Map.of("a", 1) Comparator.comparing(...) などはこの仕組みで提供されています。ファクトリーメソッド や ヘルパー関数 を interface に同居させたいときに便利です。
staticメソッドはinterface.メソッド名形式でしか呼べないので、Greeter.english()のように書きます。実装クラスのインスタンスからは呼べないことに注意してください (new EnglishGreeter().english()はコンパイルエラー)。
よくある間違い
ここで、default メソッドを使い始めた人がよくハマる落とし穴を 3 つ紹介します。
defaultだらけでinterfaceが肥大化する — 便利だからといって、振る舞いの 90% をdefaultで書いてしまうと、もはやinterfaceではなくabstract classのような重い存在になります。interfaceは「実装すべきメソッドの契約」を表すのが基本なので、defaultは「後から追加する後方互換ヘルパー」「複数の実装が共通して使う簡単な定型処理」に絞るのがコツです- 多重継承の衝突を放置する — 2 つの
interfaceが同じdefaultを持つと、コンパイルエラーが必ず出ます。Greeter.super.greet(name)構文を覚えて、衝突したときは実装クラス側で明示的にoverrideする習慣を付けましょう。エラーメッセージはclass X inherits unrelated defaultsのように出ます abstract classと混同してフィールドを持たせようとする —interfaceにはpublic static finalの定数しか書けません。private int count;のようにフィールドを書こうとするとコンパイルエラーになります。状態を持ちたいならabstract classを選ぶか、実装クラス側にフィールドを置いてください
もう 1 つ細かい話として、default メソッドを private にできるのは Java 9 以降だ、というのも覚えておくと便利です。interface 内の private メソッドは、複数の default メソッドから共通して呼ぶヘルパー関数を切り出すために使えますが、外からも実装クラスからも見えない完全な内部ヘルパーになります。
振る舞いの設計に迷ったときは「契約だけなら
interface+abstractメソッド」「契約 + 共通ヘルパーならinterface+default」「状態を持つテンプレートならabstract class」と覚えておくと、ほとんどのケースで迷わなくなります。
やってみよう
それでは課題に取り組みましょう。Solution.greetEnglish(String name) を書いて、"Hello, " + name を返すようにします。やることは次のとおりです。
Solutionの中にinterface Greeterを定義し、default String greet(String name)を"Hello, " + nameを返す形で書くGreeterをimplementsするEnglishGreeterクラス (中身は空でも OK) を用意するgreetEnglish(String name)の中でEnglishGreeterのインスタンスを作り、greet(name)を呼んでその戻り値をreturnする
テストでは greetEnglish("Java") → "Hello, Java" 以外に greetEnglish("Alice") → "Hello, Alice" のような別の引数でも呼ばれます。"Hello, Java" のように ハードコード していると別の名前で fail するので、必ず default 側の連結ロジックを通すようにしてください。スペースの位置も大事で、"Hello,Java" のようにカンマ後のスペースを抜くと比較に失敗します。
慣れてきたら、JapaneseGreeter を追加して "こんにちは, " + name を返すように override してみたり、2 つ目の interface を作って default を衝突させて Greeter.super.greet(name) で解決する練習をしてみてください。次のレッスンでは、functional interface と ラムダ式 を組み合わせて、Greeter のような小さな interface を 1 行で実装する書き方に踏み込んでいきます。
よくある質問
Q. 中級の内容は実務でどれくらい使いますか?
A. Collection(List/Map/Set)、Stream、例外処理、ジェネリクスは毎日のように登場します。Date/Time API、Files、try-with-resources も実務で頻出するため、本コースの内容は実プロジェクトでそのまま役立ちます。
Q. Stream と for ループはどっちで書くべき?
A. 可読性で選んでください。filter + map + collect が綺麗にハマるなら Stream、副作用や複雑な分岐が多いなら for ループの方が読みやすいです。一律にどちらを使うべきという正解はなく、チームのコーディング規約に合わせるのが現実的です。
Q. 次のステップでは何を学ぶべきですか?
A. 中級の基礎が固まったら、Spring Boot で Web API を作る、JUnit でテストを書く、Maven/Gradle でビルドを管理する、といった実プロジェクトのスキルに進むと効果的です。OSS のコードを読む経験も大きく成長を促します。
次のレッスン
次は instanceof と型パターン で、instanceof と型パターン を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- default の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. default とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
Solutionの中にinterface Greeterを定義し、default String greet(String name)を実装することGreeterをimplementsするEnglishGreeterクラスを用意すること (中身は空でよい)greetEnglish(String name)の中でEnglishGreeterのインスタンスを生成し、greet(name)の戻り値をreturnすること
入出力例
test-cases.txt
greetEnglish("Java") → "Hello, Java"
greetEnglish("Alice") → "Hello, Alice"
greetEnglish("") → "Hello, "