什么是双大括号初始化语法({{…}})在Java?


当前回答

你是说像这样的事?

List<String> blah = new ArrayList<String>(){{add("asdfa");add("bbb");}};

这是在创建时初始化数组列表(hack)

其他回答

每当有人使用双大括号初始化时,就会有一只小猫被杀死。

除了语法相当不寻常且不是真正的惯用(当然,品味是有争议的)之外,您还不必要地在应用程序中创建了两个重大问题,我最近在博客中对此进行了更详细的讨论。

1. 你创建了太多的匿名类

每次使用双大括号初始化都会创建一个新类。例如这个例子:

Map source = new HashMap(){{
    put("firstName", "John");
    put("lastName", "Smith");
    put("organizations", new HashMap(){{
        put("0", new HashMap(){{
            put("id", "1234");
        }});
        put("abc", new HashMap(){{
            put("id", "5678");
        }});
    }});
}};

... 将产生这些类:

Test$1$1$1.class
Test$1$1$2.class
Test$1$1.class
Test$1.class
Test.class

这对您的类加载器来说是相当大的开销——毫无意义!当然,如果你只做一次,就不会花费太多初始化时间。但是如果你在整个企业应用程序中做了20000次这样的事情……那么多内存只是为了一点“语法糖”?

2. 您可能会造成内存泄漏!

如果您使用上述代码并从一个方法返回该映射,那么该方法的调用者可能会毫无疑问地持有无法被垃圾收集的非常重的资源。考虑下面的例子:

public class ReallyHeavyObject {

    // Just to illustrate...
    private int[] tonsOfValues;
    private Resource[] tonsOfResources;

    // This method almost does nothing
    public Map quickHarmlessMethod() {
        Map source = new HashMap(){{
            put("firstName", "John");
            put("lastName", "Smith");
            put("organizations", new HashMap(){{
                put("0", new HashMap(){{
                    put("id", "1234");
                }});
                put("abc", new HashMap(){{
                    put("id", "5678");
                }});
            }});
        }};

        return source;
    }
}

返回的Map现在将包含对ReallyHeavyObject的封装实例的引用。你可能不想冒这个险:

图片来自http://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/

3.你可以假装Java有地图字面量

为了回答你的实际问题,人们一直在使用这种语法来假装Java有类似map字面量的东西,类似于现有的数组字面量:

String[] array = { "John", "Doe" };
Map map = new HashMap() {{ put("John", "Doe"); }};

有些人可能会觉得这在语法上很刺激。

第一个大括号创建了一个新的匿名类,第二组大括号创建了一个实例初始化器,类似于静态块。

正如其他人指出的那样,使用它并不安全。

但是,您总是可以使用这个替代方法来初始化集合。

Java 8

List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));

Java 9

List<String> list = List.of("A", "B", "C");

你可以把一些Java语句作为循环来初始化集合:

List<Character> characters = new ArrayList<Character>() {
    {
        for (char c = 'A'; c <= 'E'; c++) add(c);
    }
};

Random rnd = new Random();

List<Integer> integers = new ArrayList<Integer>() {
    {
         while (size() < 10) add(rnd.nextInt(1_000_000));
    }
};

但这种情况对性能有影响,请检查此讨论

这似乎与flash和vbscript中流行的with关键字相同。这是一种改变事物的方法,仅此而已。

我认为有必要强调的是,在Java中没有“双大括号初始化”这样的东西。甲骨文网站没有这个术语。在这个例子中,有两个特性一起使用:匿名类和初始化程序块。开发人员似乎已经忘记了旧的初始化程序块,并在这个主题中引起了一些混乱。引用自Oracle文档:

实例变量的初始化块看起来就像静态初始化块,但是没有static关键字:

{
    // whatever code is needed for initialization goes here
}