What is this constructor call with following doubl

2020-07-02 12:11发布

问题:

Unfortunately I haven't coded Java for about five years and I absolutely can not remember how or why the following code is working.

I stumbled across a similar example and broke it down to this. The emphasis is on the part below the comment: I don't get the constructor notation followed by the block in double brackets. And unfortunately I can not find anything in the Java documentation or by using Google (what word(s) should I google?).

package syntaxtest;

public class Main {

    public static void main(String[] args) {

        // What kind of notation is this?
        MyTest tester = new MyTest() {{
            setName("John Johnson");
        }};

        System.out.println(tester.getName());
    }
}


class MyTest {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

So here are my questions:

  1. How is this notation/syntax called?
  2. Where can I read some documentation about it?

I guess/ hope I will be able to answer the second question by myself if somebody can provide me with the answer to the first question.

To make it clear: I know the output is John Johnson ;) But I don't know why it is working.

回答1:

This is known as double brace initialization:

The first brace creates a new AnonymousInnerClass, the second declares an instance initializer block that is run when the anonymous inner class is instantiated. This type of initializer block is formally called an "instance initializer", because it is declared within the instance scope of the class -- "static initializers" are a related concept where the keyword static is placed before the brace that starts the block, and which is executed at the class level as soon as the classloader completes loading the class (specified at http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.6) The initializer block can use any methods, fields and final variables available in the containing scope, but one has to be wary of the fact that initializers are run before constructors.

This only works only for non-final classes because it creates an anonymous subclass.



回答2:

Let's layout the code a bit differently:

MyTest tester = new MyTest() {
  {
    setName("John Johnson");
  }
};

What you see here is called double brace initialization. You have an anonymous inner subclass of class MyTest, along with an initializer block, which is a block that contains code that is run when the object is constructed.

Normally, you would put such code in the constructor, but since anonymous inner classes can't have constructors, this is the only way to guarantee the code is run when it's supposed to.

Having said that, it's a bit ugly to do this. There are better ways. However, I do use it myself on occasion, usually in the following idiom to create an immutable map:

final Map<String, Integer> textToInt = Collections.unmodifiableMap(new HashMap<String, Integer>() {{
    put("one", 1);
    put("two", 2);
    // etc
}});

Which creates a new map, overrides it, adds some values to it in the initializer block, and wraps it in an unmodifiable map.



回答3:

MyTest tester = new MyTest() {{
   setName("John Johnson");
}};

is the same as

MyTest tester = new MyTest();
tester.setName("John Johnson");