新的Class(...){{...}}初始化习惯用法的含义是什么?(涉及IT技术)

47

以下代码中的{{ ... }}块是什么意思?

class X {

    private Y var1;

    private X() {
        Z context = new Z(new SystemThreadPool()) {{
            var1 = new Y();
        }};
    }

}

5
我想在Google上搜索双大括号可能不容易。 - Tom Hawtin - tackline
3
你可以在谷歌上搜索“双括号”来了解更多信息。 - Ates Goral
4个回答

71

这被称为双花括号初始化。(编辑:链接已删除,在此处存档)

它意味着您正在创建一个匿名子类,双括号内的代码基本上是一个构造函数。它经常用于向集合中添加内容,因为Java的语法实际上是集合常量的创建方式有些笨拙。

所以你可能会这样做:

List<String> list = new ArrayList<String>() {{
  add("one");
  add("two");
  add("three");
}};

而不是:

List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

我实际上不喜欢那样,更喜欢这样做:

List<String> list = Arrays.asList("one", "two", "three");

因此,在这种情况下,这并没有太多意义,而对于例如地图这样没有方便的帮助程序的东西来说,则有意义。


@skaffman 在“流畅的Java”中非常有用,比如JATL的尝试。 - C. Ross
8
请注意,Arrays.asList()返回的是java.util.Arrays.ArrayList而不是功能有限的java.util.ArrayList。 - Asaf
3
这项技术有些棘手,并且有一些需要注意的地方。可能会导致equals()方法无法正常工作或增加内存使用,但没有什么好的理由。除非您确切知道自己在做什么,否则最好避免使用双括号初始化。请参见文章获取更多详细信息。 - Andrii Polunin

12

“外部”大括号表示你正在创建一个匿名子类,第二对大括号是对象初始化器。初始化器在类的构造函数之前运行,但在任何super调用之后(因此也在任何超类初始化器之后)。您还可以在非匿名类中使用初始化器,这是一种方便的方法来初始化final字段,如果您有多个不能相互调用的构造函数或需要比通常的字段初始化器更复杂的初始化的字段。

考虑下面这个类:

class X extends Y{
    private final int lulz;

    private static boolean someCondition(){...}
    private static boolean danger() throws SomeException { ... }
    public X(A a) throws SomeException {
        super(a); 
        lulz = someCondition()? danger() : 0;
    }
    public X(B b) throws SomeException {
        super(b); 
        lulz = someCondition()? danger() : 0;
    }
}

它可以重写为:

class X extends Y{
    private final int lulz;

    private static boolean someCondition(){...}
    private static boolean danger() throws SomeException { ... }
    { // initalizer -- might throw SomeException!
        lulz = someCondition()? danger() : 0;
    }
    public X(A a) throws SomeException { super(a); }
    public X(B b) throws SomeException { super(b); }
}

如果初始化程序可能会抛出受检异常,所有的构造函数都必须声明它们可以抛出该异常。


4
你正在创建一个匿名类,并使用类实例初始化的习惯用语,就像这样:

anonymous classclass Instance initializer idiom。

class X {
    private Y var1;

    private X() {
        Z context = new Z(
               new SystemThreadPool()) {
                   {                        // This is the initialize idiom
                       var1 = new Y();      //
                   }                        //
               }
          );  // BTW you are missing ")"
    }
}

3

如之前的答案所提到的,双大括号初始化是正确的。

它使用了一种特殊的技术来在Java中初始化实例成员。这是一种简写方式,用于在类定义中定义一个共享的代码块,当任何一个类构造函数被激活时都会运行。

我添加了官方Java文档的链接,以便更全面地了解该主题。

文档中:

Initializer blocks for instance variables look just like static initializer blocks, but without the static keyword:

{

// whatever code is needed for initialization goes here 

}

The Java compiler copies initializer blocks into every constructor. Therefore, this approach can be used to share a block of code between multiple constructors.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接