1. 前言
在 Java 8 之前,介面純粹是「契約」:只有抽象方法,沒有任何實作、沒有任何邏輯,只有承諾。而自 Java 8 起,介面變得更「自給自足」:它們不僅可以包含 default 方法,還能包含 static 方法。
介面中的 static 方法屬於介面本身,而不是其實作(類別)。它們不需要建立物件,並且只能直接透過介面名稱呼叫。
類比:
介面中的 static 方法就像貼在辦公室牆上的參考手冊:每位員工(類別)都可以使用,但手冊本身不屬於任何一位員工。
介面中的 static 方法讓我們能將與該介面相關的輔助功能進行分組,而不會汙染實作類別的命名空間。
介面中 static 方法的語法
static 方法在介面內用關鍵字 static 宣告。它們必須包含實作(花括號內的程式碼),而且只能透過介面名稱呼叫。
範例:
public interface MathUtils {
static int sum(int a, int b) {
return a + b;
}
static double average(int a, int b) {
return (a + b) / 2.0;
}
}
呼叫介面的 static 方法:
int result = MathUtils.sum(5, 7); // 12
double avg = MathUtils.average(10, 20); // 15.0
注意:
無法透過實作類別或其物件來呼叫介面的 static 方法!只能透過介面名稱。
2. 介面的 static 方法與 default 方法有何不同?
Static 方法:
- 屬於介面本身。
- 不會被實作類別繼承。
- 不可透過類別的物件呼叫。
- 不可在實作類別中覆寫。
- 只能透過介面名稱呼叫。
default 方法:
- 屬於實作該介面的類別之物件(實例)。
- 可在實作類別中被覆寫。
- 可透過實作類別的物件呼叫。
- 會被實作類別繼承。
總結:default 方法擴充的是物件的能力,而 static 方法擴充的是介面本身。
對比範例:
interface Printer {
default void print(String text) {
System.out.println("Default: " + text);
}
static void info() {
System.out.println("Printer interface v1.0");
}
}
class ConsolePrinter implements Printer {}
public class Main {
public static void main(String[] args) {
Printer.info(); // 透過介面呼叫 static 方法
ConsolePrinter cp = new ConsolePrinter();
cp.print("Hello!"); // 透過物件呼叫 default 方法
// cp.info(); // 錯誤!static 方法不能透過物件呼叫
}
}
3. 為什麼需要在介面中使用 static 方法?
在介面中還沒有 static 方法之前,如果你想加入與介面相關的工具函式,就得建立帶有 Utils 或 Helper 後綴的獨立類別:
public interface Movable {
void move(int x, int y);
}
public class MovableUtils {
public static void resetPosition(Movable m) {
m.move(0, 0);
}
}
現在可以直接在介面裡完成:
public interface Movable {
void move(int x, int y);
static void resetPosition(Movable m) {
m.move(0, 0);
}
}
這讓程式碼更合乎邏輯且更具關聯性:與介面相關的方法,現在就住在該介面中。
優點:
- 將實用函式分組並放在介面契約旁邊。
- 不會「汙染」實作類別的命名空間。
- 提升可讀性與可維護性。
4. 介面中 static 方法的限制與特性
介面的 static 方法不會被實作類別繼承。
不能透過實作類別的物件或類別名稱呼叫。只能透過介面名稱!
介面中的 static 方法一定包含實作:不能是 abstract 或 default。
它們總是包含實作。
static 方法不能存取介面的非靜態方法或變數。
它們只能存取介面的其他 static 成員(例如 static final 常數)。
介面的 static 方法可以是 private(Java 9+)。
可以建立輔助性的 private static 方法供介面內部使用。
5. 範例:為 Movable 介面加入靜態方法
來看看如何在 Movable 介面中加入 static 方法。假設我們有一個 Movable 介面,且由不同的類別(例如機器人、動物、交通工具)來實作。
步驟 1:宣告包含 static 方法的介面:
public interface Movable {
void move(int x, int y);
static void resetPosition(Movable obj) {
obj.move(0, 0);
}
static double distance(int x1, int y1, int x2, int y2) {
int dx = x2 - x1;
int dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
}
步驟 2:在類別中實作該介面:
public class Robot implements Movable {
private int x, y;
public Robot(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public void move(int x, int y) {
System.out.println("機器人移動到座標 (" + x + "," + y + ")");
this.x = x;
this.y = y;
}
public void printPosition() {
System.out.println("目前位置:(" + x + "," + y + ")");
}
}
步驟 3:使用介面的 static 方法:
public class Main {
public static void main(String[] args) {
Robot robby = new Robot(10, 15);
robby.printPosition();
// 透過介面的 static 方法重設位置
Movable.resetPosition(robby);
robby.printPosition();
// 透過介面的 static 方法計算兩點距離
double dist = Movable.distance(0, 0, 10, 15);
System.out.println("距離:" + dist);
}
}
輸出結果:
目前位置:(10,15)
機器人移動到座標 (0,0)
目前位置:(0,0)
距離:18.027756377319946
請注意:
我們呼叫的是 Movable.resetPosition(robby),而不是 robby.resetPosition()。static 方法非常適合用於對介面本身在邏輯上相關、但不屬於特定物件的操作。
6. 介面中的 private static 方法
有時在介面內需要只供內部使用的輔助方法(例如避免在多個 static 或 default 方法中重複程式碼)。自 Java 9 起,介面支援 private static 方法。
範例:
public interface Logger {
static void logInfo(String message) {
log("INFO", message);
}
static void logError(String message) {
log("ERROR", message);
}
private static void log(String level, String message) {
System.out.println("[" + level + "] " + message);
}
}
現在 log() 無法從介面外部存取,但可在其他 static 方法內部使用。
7. 標準程式庫中的 static 方法在哪裡?
在標準的 Java 程式庫中,介面的 static 方法被廣泛使用,特別是在集合與函式式介面中。
範例:
- Comparator.comparing()、Comparator.reverseOrder() — 介面 Comparator 的 static 方法。
- Predicate.isEqual() — 介面 Predicate 的 static 方法。
- List.of()、Set.of()、Map.of()(Java 9+)— 用於建立不可變集合的 static 方法。
Comparator 範例:
import java.util.Comparator;
public class Main {
public static void main(String[] args) {
Comparator<String> cmp = Comparator.reverseOrder();
int res = cmp.compare("a", "b"); // 正數,因為在反向排序中「a」 > 「b」
System.out.println(res);
}
}
8. 使用介面 static 方法時的常見錯誤
錯誤 1:嘗試透過實作類別的物件來呼叫 static 方法。
這行不通!介面的 static 方法只能透過介面名稱呼叫,例如 Movable.resetPosition(obj),而不是 obj.resetPosition()。
錯誤 2:嘗試在實作類別中覆寫介面的 static 方法。
static 方法不會被繼承,也不能被覆寫!如果你在類別中宣告同名的 static 方法,那會是完全不同、與介面無關的方法。
錯誤 3:忘了 static 方法不能存取非靜態成員。
介面的 static 方法只能使用 static 成員(例如 static final 常數),不能存取非靜態的方法或變數。
錯誤 4:與 default 方法混淆。
default 方法透過物件呼叫,static 方法只能透過介面名稱呼叫。別搞混了!
GO TO FULL VERSION