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();
}
語法特點:
- 區域類別可以在任何方法、建構子,甚至初始化區塊內宣告。
- 區域類別必須有名稱(不同於匿名類別)。
- 區域類別不能使用存取修飾詞(public、private、protected)或 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. 區域類別的限制與細節
- 存取修飾詞:區域類別不能宣告為 public、private 或 protected。也不能加上 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。
區域類別不能用 public、private、protected 或 static 來宣告。若你這麼做——編譯器會拒絕。
錯誤第 3 條:嘗試在方法之外使用區域類別。
區域類別只存在於其宣告的方法(或區塊)內。從其他方法或類別外部無法存取。
錯誤第 4 條:以區域類別承載過於複雜的邏輯。
如果你的區域類別變得過大,包含太多方法或欄位——多半應該把它抽出成獨立類別。區域類別適合用於精簡的、輔助性的任務。
GO TO FULL VERSION