CodeGym /課程 /JAVA 25 SELF /區域類別:在方法內宣告

區域類別:在方法內宣告

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

1. 區域類別

區域類別——是在方法內宣告的類別(甚至可以在程式碼區塊內,例如在迴圈或 if 區塊中)。它只在該方法內可見與可用,離開方法之後——就像它不存在一樣。與匿名類別不同,區域類別有名稱(即便是簡單名稱),可以包含多個方法,甚至欄位。

打個比方——想像你按食譜在做一道複雜料理。其中一步需要調製一款只在此料理中使用的特製醬汁。與其為了這個醬汁另出一本食譜,你只需在主食譜中直接描述其做法即可。區域類別的運作方式完全相同——它是「食譜中的食譜」,只在某個特定方法中需要。

區域類別宣告範例

public class Outer {
    void someMethod() {
        class Local {
            void print() {
                System.out.println("Hello from Local!");
            }
        }
        Local local = new Local();
        local.print();
    }
}

這裡的 Local 類別是在 someMethod 方法(隸屬於 Outer 類別)內宣告。它只存在於此方法中,對外部不可見。

區域類別的語法

區域類別的宣告類似一般類別,只是發生在方法內:

void myMethod() {
    class MyLocalClass {
        void doSomething() {
            System.out.println("區域類別正在運作!");
        }
    }

    MyLocalClass obj = new MyLocalClass();
    obj.doSomething();
}

語法特點:

  • 區域類別可以在任何方法、建構子,甚至初始化區塊內宣告。
  • 區域類別必須有名稱(不同於匿名類別)。
  • 區域類別不能使用存取修飾詞(publicprivateprotected)或 static 修飾詞。
  • 區域類別可以實作介面或繼承其他類別。

類別型別的比較:

public class Example {
// 1. 一般類別(在獨立檔案中)
// class RegularClass { ... }

    // 2. 內部類別(在類別中)
    class InnerClass { }
    
    // 3. 靜態巢狀類別(在類別中)
    static class NestedClass { }
    
    void method() {
        // 4. 區域類別(在方法中)
        class LocalClass { }
        
        // 5. 匿名類別(在方法中,沒有名稱)
        Object obj = new Object() { };
    }
}

區域類別 = 僅供此方法使用的具名類別!

2. 區域類別的特性

對方法變數的存取

區域類別可以存取:

  • 外部類別的欄位(即使它們是 private)。
  • 僅限於該方法中為 final 或 effectively final 的變數

「effectively final」是什麼?
指的是在初始化後其值未再被改變的變數。例:

void foo() {
    int x = 5; // effectively final
    class L { void print() { System.out.println(x); } }
}

若你之後寫下 x = 10;,編譯器就會抱怨。

為什麼會這樣?

這與 Java 在底層如何實作區域類別有關:方法變數其實是被複製進區域類別內,而不是以參照保存。若該變數可以更動,區域類別可能會操作到過期的副本——這會導致錯誤與頭痛。

存取變數的範例

public class Outer {
    private String secret = "祕密!";

    void revealSecret() {
        String greeting = "你好,"; // effectively final

        class Revealer {
            void printSecret() {
                System.out.println(greeting + " " + secret);
            }
        }

        Revealer revealer = new Revealer();
        revealer.printSecret();
    }
}

3. 區域類別的用途:何時使用?

區域類別不是天天都要用,但有時能讓程式更乾淨、更精簡。以下是適合使用的時機:

  • 只針對單一方法的輔助邏輯。
    例如在方法中需要實作複雜排序、過濾或暫時性的資料結構。
  • 封裝,避免用多餘的型別把類別「弄髒」。
    如果某個類別只在一處需要,何必讓它在整個類別或套件中可見?
  • 實作設計模式,而輔助物件只需要在區域使用。

範例:使用區域類別排序

假設你有一個名字清單,想依長度排序。可以建立一個區域類別的比較器:

import java.util.*;

public class NameSorter {
    public void sortNames(List<String> names) {
        class LengthComparator implements Comparator<String> {
            @Override
            public int compare(String a, String b) {
                return Integer.compare(a.length(), b.length());
            }
        }
        Collections.sort(names, new LengthComparator());
    }
}

範例:暫時性結構

有時需要在單一方法中建立輔助結構來處理資料:

void processScores(int[] scores) {
    class ScoreInfo {
        int score;
        boolean passed;

        ScoreInfo(int score) {
            this.score = score;
            this.passed = score >= 60;
        }
    }

    for (int s : scores) {
        ScoreInfo info = new ScoreInfo(s);
        System.out.println("分數:" + info.score + ",通過:" + info.passed);
    }
}

4. 區域類別 vs 匿名類別

有時會問:既然有匿名類別,為什麼還需要區域類別?我們來看看差異。

  • 區域類別——具名類別,可以在方法中多次使用,能加入欄位、數個方法,甚至巢狀類別。
  • 匿名類別——沒有名稱,通常用於一次性的介面實作或繼承者,往往只包含一個方法(或覆寫一兩個方法)。

何時使用:

  • 若邏輯簡單且只需要一次——使用匿名類別。
  • 若需要更多方法或欄位,或會在方法內多處重用——使用區域類別。

比較範例

匿名類別:

Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("快速且匿名!");
    }
};

區域類別:

void doWork() {
    class MyWorker implements Runnable {
        @Override
        public void run() {
            System.out.println("我是區域類別!");
        }
    }
    MyWorker worker = new MyWorker();
    worker.run();
}

5. 區域類別的限制與細節

  • 存取修飾詞:區域類別不能宣告為 publicprivateprotected。也不能加上 static
  • 靜態成員:區域類別不能包含靜態成員,常數(static final)除外。
  • 作用域:區域類別只在其宣告的區塊內可見。
  • 泛型參數的使用:若有需要,區域類別可以是泛型。
  • 巢狀:可以在區域類別內再宣告區域類別(但別濫用,否則程式碼會像俄羅斯套娃)。

6. 在真實應用中的區域類別

讓我們繼續擴充教學用應用程式。假設有個 Quiz 類別,會向使用者提問並檢查答案。我們想在檢查方法內建立一個暫時性類別,用來儲存使用者的答案與檢查狀態。

範例:在應用中使用區域類別

import java.util.Scanner;

public class Quiz {
    private String question = "法國的首都是什麼?";
    private String correctAnswer = "巴黎";

    public void runQuiz() {
        Scanner scanner = new Scanner(System.in);

        System.out.println(question);
        String userAnswer = scanner.nextLine();

        // 用於儲存結果的區域類別
        class AnswerResult {
            String answer;
            boolean isCorrect;

            AnswerResult(String answer) {
                this.answer = answer;
                this.isCorrect = answer.equalsIgnoreCase(correctAnswer);
            }

            void printResult() {
                if (isCorrect) {
                    System.out.println("正確!");
                } else {
                    System.out.println("不正確,正確答案是:" + correctAnswer);
                }
            }
        }

        AnswerResult result = new AnswerResult(userAnswer);
        result.printResult();
    }
}

這裡的 AnswerResult 類別只存在於 runQuiz 方法內,其他地方都不需要。這是區域類別在實務上的極佳示例!

7. 使用區域類別的常見錯誤

錯誤第 1 條:嘗試存取非 final 或非 effectively final 的方法變數。
若你在方法中宣告了變數,接著在區域類別中使用後又修改了它的值,編譯器會立刻報錯。務必確保這類變數在初始化後不再變更。

錯誤第 2 條:想要加入存取修飾詞或 static。
區域類別不能用 publicprivateprotectedstatic 來宣告。若你這麼做——編譯器會拒絕。

錯誤第 3 條:嘗試在方法之外使用區域類別。
區域類別只存在於其宣告的方法(或區塊)內。從其他方法或類別外部無法存取。

錯誤第 4 條:以區域類別承載過於複雜的邏輯。
如果你的區域類別變得過大,包含太多方法或欄位——多半應該把它抽出成獨立類別。區域類別適合用於精簡的、輔助性的任務。

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