Java 集合框架缺乏字面量語法,構建常量集合往往需要「聲明變量 → new 實例 → 多次 add → 傳參」四步。雙大括號初始化 利用匿名內部類 + 實例初始化塊,將聲明與初始化合為一句,顯著提升可讀性。
new HashSet<String>() {{
add("XZ13s");
add("AB21/X");
add("YYLEX");
add("AR2D");
}};
| 結構 | 本質 | 執行時機 |
|---|---|---|
外層 {} | 匿名內部類(繼承 HashSet) | 類加載/實例化時 |
內層 {{}} | 實例初始化塊 | 構造器之前,每次 new 都執行 |
等價於:
new HashSet<String>() {
{ // 實例初始化塊
add("XZ13s"); ...
}
};
Map<String, Set<String>> baselineMap = new HashMap<>();
baselineMap.put(type, new HashSet<String>() {{ add(schema); }});
// 或合併現有集合
baselineMap.put(type, new HashSet<String>(baselineMap.get(type)) {{ add(schema); }});
add(new JPanel() {{
setLayout(new BorderLayout());
setBorder(BorderFactory.createTitledBorder("設定"));
add(new JLabel("名稱:"), BorderLayout.WEST);
add(new JTextField(20), BorderLayout.CENTER);
}});
| 風險 | 說明 | 緩解方案 |
|---|---|---|
| 持有外部類引用 | 匿名內部類隱式持有 OuterClass.this | 靜態上下文使用;或改用靜態工廠方法 |
| 序列化失敗 | 外部類未實現 Serializable 會導致 NotSerializableException | 確保外部類可序列化;或改用普通初始化 |
| 類爆炸 | 每次調用生成一個匿名類 .class 文件 | 僅用於常量/啟動期初始化,避免高頻調用熱點 |
| final 類不可用 | String、Integer 等 final 類無法繼承 | 改用 List.of()、Set.of()、Map.of()(Java 9+) |
| equals/hashCode 異常 | 匿名子類可能破壞父類 equals 契約 | 不將雙大括號實例作為 Map 鍵/Set 元素 |
// 不可變集合字面量語法
List.of("a", "b", "c");
Set.of("x", "y");
Map.of("k1", "v1", "k2", "v2");
Map.ofEntries(entry("k1","v1"), entry("k2","v2"));
// 可變集合單表達式
new ArrayList<>(List.of("a", "b", "c"));
new HashSet<>(Set.of("x", "y"));
new HashMap<>(Map.of("k1", "v1"));
結論:Java 9+ 項目優先使用標準工廠方法;維舊項目或需可變集合單表達式時,雙大括號仍是實用技巧,但務必了解其副作用。