CodeGym /課程 /JAVA 25 SELF /介面中的 static 方法

介面中的 static 方法

JAVA 25 SELF
等級 21 , 課堂 3
開放

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 方法之前,如果你想加入與介面相關的工具函式,就得建立帶有 UtilsHelper 後綴的獨立類別:

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 方法只能透過介面名稱呼叫。別搞混了!

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION