1. 概述
本教程将展示如何使用双大括号在单个Java表达式中创建并初始化对象。我们也会分析为什么这种技术被视为反模式。
2. 标准实现方式
通常我们初始化并填充国家集合时,会这样写:
@Test
public void whenInitializeSetWithoutDoubleBraces_containsElements() {
Set<String> countries = new HashSet<String>();
countries.add("India");
countries.add("USSR");
countries.add("USA");
assertTrue(countries.contains("India"));
}
从上述代码可以看出,我们执行了三个步骤:
- 创建 HashSet 实例
- 向 HashSet 添加国家
- 最后验证国家是否存在于 HashSet 中
3. 使用双大括号
实际上,我们可以将创建和初始化合并到单个语句中——这就是双大括号的应用场景:
@Test
public void whenInitializeSetWithDoubleBraces_containsElements() {
Set<String> countries = new HashSet<String>() {
{
add("India");
add("USSR");
add("USA");
}
};
assertTrue(countries.contains("India"));
}
这段代码的实际执行过程:
- 创建一个继承自 HashSet 的匿名内部类
- 在实例初始化块中调用 add 方法添加国家名称
- 最后验证国家是否存在于 HashSet 中
4. 双大括号的优势
使用双大括号有几个简单优势:
✅ 相比原生方式代码行数更少
✅ 代码可读性更高
✅ 创建和初始化在同一表达式中完成
5. 双大括号的劣势
但双大括号存在明显缺陷:
❌ 初始化方式晦涩难懂,非主流用法
❌ 每次使用都会创建额外匿名类
❌ 不支持Java 7引入的“菱形运算符”
❌ 当目标类被标记为 final 时无法使用
❌ 持有对外部实例的隐藏引用,可能导致内存泄漏
正是这些缺陷导致双大括号初始化被视为反模式。
6. 替代方案
6.1 Stream工厂方法
我们可以改用Java 8的Stream API初始化集合:
@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
Set<String> countries = Stream.of("India", "USSR", "USA")
.collect(collectingAndThen(toSet(), Collections::unmodifiableSet));
assertTrue(countries.contains("India"));
}
6.2 Java 9集合工厂方法
Java 9提供了更简洁的工厂方法:
List<String> list = List.of("India", "USSR", "USA");
Set<String> set = Set.of("India", "USSR", "USA");
更多细节可参考这篇文章。
7. 总结
本教程探讨了双大括号初始化的使用方式及其优缺点。所有示例代码可在GitHub项目中找到——这是一个基于Maven的项目,可直接导入运行。