Java 8 引入 Lambda 表達式,亟需增強集合庫 (如 List.forEach())。但接口一旦發布,無法在不破壞現有實現類的前提下添加新方法。默認方法 解決了這個「接口演進難題」:在接口中提供方法默認實現,實現類可選擇繼承或覆寫。
public interface Vehicle {
// 默認方法:用 default 修飾,有方法體
default void printType() {
System.out.println("I am a vehicle");
}
// 靜態方法:接口級工具方法,不可被實現類繼承
static String getCategory() {
return "Transport";
}
// 抽象方法:仍需實現類實現
void move();
}
default 方法隱式 public,可被實現類直接調用InterfaceName.super.method() 顯式調用父接口默認實現Object 類方法 (equals/hashCode/toString)InterfaceName.staticMethod() 調用interface A {
default void hello() { System.out.println("A"); }
}
interface B {
default void hello() { System.out.println("B"); }
}
// 編譯錯誤:繼承了不相關的默認方法
class C implements A, B { }
// 必須顯式解決衝突
class D implements A, B {
@Override
public void hello() {
// 選項 1: 自己實現
System.out.println("D");
// 選項 2: 調用指定接口默認實現
A.super.hello(); // 輸出 "A"
// B.super.hello(); // 輸出 "B"
}
}
@FunctionalInterface
public interface Iterable {
Iterator iterator();
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
}
List list = Arrays.asList("Java", "Groovy", "Kotlin");
// Lambda 表達式
list.forEach(s -> System.out.println(s));
// 方法引用
list.forEach(System.out::println);
// 內部實現相當於:
for (String s : list) {
System.out.println(s);
}
interface Collection extends Iterable {
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
default Stream parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
}
| 調用場景 | 字節碼指令 | 說明 |
|---|---|---|
| 接口引用調用默認方法 | invokeinterface | 多態分發,運行時確定實現 |
| 類引用調用默認方法 (未覆寫) | invokevirtual | 直接調用接口默認實現 |
| 顯式調用父接口默認方法 | invokespecial | 創新點:指定接口而非父類 |
// 字節碼示例
public void hello() {
A.super.hello(); // invokespecial #2 // InterfaceMethod A.hello:()V
}
@implSpec 標籤說明默認行為,指導覆寫者Collection.emptyList() 替代 Collections.emptyList()| 特性 | 默認方法 (接口) | 抽象類 |
|---|---|---|
| 多實現 | 類可實現多個接口 | 單繼承 |
| 構造器 | 無 | 有 |
| 實例字段 | 無 (只能 static final) | 有 |
| 訪問修飾符 | 隱式 public | 任意 |
| 適用場景 | 能力擴展、庫演進、多重組合 | 代碼復用、狀態共享、模板方法 |
默認方法是 Java 8 為了 Lambda 化集合庫而引入的「接口演進機制」,通過 default 關鍵字在接口中提供可選實現。它解決了「接口發布後無法添加方法」的歷史包袱,配合 invokespecial 字節碼指令實現顯式父接口調用。核心價值在於保持二進制兼容性的前提下擴展接口功能。日常開發中,理解其衝突解決規則、字節碼機制、與抽象類的區別,有助於設計更健壯的 API。