J2SE 1.5(Tiger)引入的增強 for 循環(for-each 循環)極大地簡化了集合與陣列的遍歷操作。本文將深入探討兩個進階主題:如何使用 final 修飾符防止循環變數被意外修改,以及循環變數類型與被遍歷對象元素類型之間的相容性規則。
在預設情況下,編譯器允許在增強 for 循環的循環體內對循環變數進行重新賦值。然而,這種操作並不會影響循環外部的變數,反而容易造成程式碼閱讀困難,因此不建議這樣做。
Java 提供了一種編譯期檢查機制:在循環變數類型前加上 final 修飾符。一旦加上 final,任何在循環體內對循環變數的賦值操作都會導致編譯錯誤,從而有效杜絕有意或無意的修改行為。
int[] integers = {1, 2, 3, 4};
for (final int i : integers) {
i = i / 2; // 編譯時出錯
}
注意: final 僅禁止對循環變數本身的重新賦值(即改變引用指向)。若循環變數為物件引用,仍可呼叫其方法修改物件內部狀態,或對其屬性賦值。
Random[] randoms = new Random[]{new Random(1), new Random(2), new Random(3)};
for (final Random r : randoms) {
r.setSeed(4); // 將所有 Random 物件設為相同種子
System.out.println(r.nextLong()); // 種子相同,首個結果也相同
}
為確保循環變數在每次迭代開始時都能安全賦值,J2SE 1.5 對循環變數類型施加了限制。合法的類型選擇包括:
最直觀的用法:循環變數類型與被遍歷對象的元素類型一致。
int[] integers = {1, 2, 3, 4};
for (int i : integers) {
System.out.println(i); // 依次輸出 1、2、3、4
}
循環變數可宣告為元素類型的父類別或實作介面。例如用 Object 遍歷 Collection<String>,或用 int 遍歷 byte[](基本類型擴大轉換)。
String[] strings = {"A", "B", "C", "D"};
Collection<String> list = java.util.Arrays.asList(strings);
for (Object str : list) {
System.out.println(str); // 依次輸出 A、B、C、D
}
J2SE 1.5 引入自動裝箱/拆箱機制,基本類型與其包裝類別可自動轉換。因此,用 Integer 遍歷 int[],或用 byte 遍歷 Collection<Byte> 均合法。
int[] integers = {1, 2, 3, 4};
for (Integer i : integers) {
System.out.println(i); // 依次輸出 1、2、3、4
}
關鍵提醒: 此處的「元素類型」由被遍歷對象的**宣告類型**決定。若為 Object[] 陣列,即便實際裝載皆為 String 物件,其元素類型仍為 Object。
J2SE 1.5 以前,Collection 無法限制儲存物件的類型,所有元素均視為 Object。泛型機制的引入解決了此問題:現在可用 Collection<T> 表示元素類型統一為 T 的集合(如 Collection<String>、Collection<Integer>)。注意:T 不能為基本類型(如 Collection<int> 非法)。
配合增強 for 循環,泛型讓遍歷更加簡潔且類型安全:
Collection<String> list = ...;
for (String s : list) { // 無需強制轉型
System.out.println(s);
}