Java:double Brace Initialization

Source: Internet
Author: User

When I first approached this product, I was exposed to the use of double brace initialization in our code. That code is used to initialize a collection:

1 Final New Hashset<string>() {{2    Add (' Alice '); 3     Add (' Bob '); 4     Add (' Marine '); 5 }};

I believe the first time I saw this way of using the same reader as I felt at the time: what is this? Of course, by adding a breakpoint at the call of the function add (), you'll see that this is actually adding elements to the collection exclusions that you just created using the Add () function.

Double Brace Initialization Introduction

But why should we initialize the set in this way? As a comparison, let's take a look at the initialization code that we usually write with the same content collection:

1 Final New Hashset<string>(); 2 exclusions.add (' Alice '); 3 exclusions.add (' Bob '); 4 exclusions.add (' Marine ');

This code is cumbersome, isn't it? When writing this code, we need to repeat the exclusions many times. At the same time, the code is annoying when software developers need to check exactly what elements are added to the collection. In turn, using double brace initialization to initialize a collection is straightforward:

1 Final New Hashset<string>() {{2    Add (' Alice '); 3     Add (' Bob '); 4     Add (' Marine '); 5 }};

Therefore, for a person familiar with the use of the method, double brace initialization clear and concise, code readability is good maintainability, nature is the first choice to initialize the set. For a person who has not been exposed to the use of the method and the foundation is not very reliable, double brace initialization is a bit obscure.

From obscurity to familiarity is actually very simple, that is to understand how it works. If you change the format of the double brace initialization example above slightly, I believe you will see some clues:

1 Final New Hashset<string>() {2    {3        Add (' Alice '); 4         Add (' Bob '); 5         Add (' Marine '); 6     }7 };

Now you can see how the double brace initialization works, right? Double brace initialization contains a total of two curly braces. The outer curly braces actually indicate that you are currently creating an anonymous class that derives from hashset<string>:

1 Final New Hashset<string>() {2     //  Each member of the anonymous derived class 3 };

The inner curly braces are actually instance initializer declared inside the anonymous derived class:

1 Final New Hashset<string>() {2    {3         ///  because a constructor cannot be added to an anonymous class. So here instance initializer4         // is actually equal to the constructor, used to initialize the current anonymous class instance 5     }6 };

When creating a collection from a double brace initialization, what we get is actually an anonymous class derived from the collection class. When the anonymous class is initialized, the instance initializer that it declares internally is executed, allowing the function to call Add () to add elements to the collection that was just created.

In fact, double brace initialization is not confined to the initialization of the collection type. In fact, any type can perform pre-initialization with it:

1 New nutritionfacts () {{2     setcalories (); 3     Setsodium (+); 4     Setcarbohydrate (+); 5 }};

See it. This is similar to the Fluent interface model I mentioned in another article.

Double Brace Initialization the pros and cons

Next, we need to understand the pros and cons of double brace initialization to better use it.

The advantages of double brace initialization are obvious: it is better readability and better maintainability for those familiar with the use of this method.

But double brace initialization also has a series of problems. The most serious possibility is that double brace initialization can cause a memory leak. When using double brace initialization, we actually created an anonymous class. The anonymous class has a property that the anonymous class instance will have a reference to the type that contains it. If we pass the anonymous class instance through a function call, and so on, to the type, the retention of the anonymous class actually causes the outer type to fail to be freed, causing a memory leak.

For example, in the Joshua Bloch version of the Builder class implementation (see this blog post), we can use double brace initialization in the build () function to generate the product instance:

1  Public classnutritionfacts {2 ...3 4      Public Static classBuilder {5 ...6          Publicnutritionfacts Build () {7             return Newnutritionfacts () {{8Setservingsize (100);9Setservings (3);Ten ... One             }}; A         } -     } -}

When a user creates a product instance from the builder, he will use the following code:

1 New NutritionFacts.Builder.setXXX () .... build ();

The above code does not maintain any references to Nutritionfacts.builder, so after executing this code, the memory that the program actually uses should only add an nutritionfacts instance, right? The answer is in the negative. Because a double brace initialization is used in the build () function, a reference to a Nutritionfacts.builder type is included in the newly created nutritionfacts instance.

Another disadvantage is that it destroys the semantics of the Equals () function. When implementing the Equals () function for a type, we may need to determine whether the two participating comparisons are of the same type:

1 @Override 2  Public Boolean equals (Object o) {3     if null && o.getclass (). Equals (GetClass ())) {4        ... 5     }6  7     returnfalse; 8 }

This kind of realization has certain controversy. The main point of contention is that Joshua Bloch in effective Java's item 8 says it violates the Richter scale replacement principle. Those who refute this view are primarily of the view that the responsibility for maintaining the correctness of the Equals () function will need to be guaranteed by the derived class. And semantically, if the types of the two classes are different, then they are equal to each other in itself is an absurd thing. Therefore, in the implementation of some class libraries, they are forced to require that the type of two instances participating in the comparison be consistent by checking the type.

When you use double brace initialization, we create an anonymous class that derives from the target type. Take the example of the build () function that was just shown:

1  Public classnutritionfacts {2 ...3 4      Public Static classBuilder {5 ...6          Publicnutritionfacts Build () {7             return Newnutritionfacts () {{8Setservingsize (100);9Setservings (3);Ten ... One             }}; A         } -     } -}

In the build () function, we are actually creating an anonymous class that derives from Nutritionfacts. If we add a breakpoint after the code, we can see from the Debug feature that the actual type of instance created by that code is nutritionfacts$1. Therefore, if the nutritionfacts equals () function internal implementation determines whether the two instances participating in the comparison have the same type, then we have just passed the double brace initialization the nutritionfacts$ A 1 type instance will certainly not be equal to its nutritionfacts instances.

Well, since we've just mentioned the representation of anonymous classes in the debugger, we need to consider this carefully. The reason is simple: in the more complex use of double brace initialization, the representation of these anonymous classes can be very difficult to read. Take the following code as an example:

1map<string, object> characterinfo =NewHashmap<string, object>() {{2Put ("FirstName", "John");3Put ("LastName", "Smith");4Put ("Children",NewHashset() {{5AddNewHashmap<string, object>() {{6Put ("FirstName", "Alice");7Put ("LastName", "Smith");8         }});9AddNewHashmap<string, object>() {{TenPut ("FirstName", "George"); OnePut ("LastName", "Smith"); A         }}); -     }}); -}};

While debugging with the debugger, you will see the following series of types:

Sample.class

Sample$1.class

Sample$1$1.class

Sample$1$1$1.class

Sample$1$1$2.class

When we look at this data, we often don't have a direct understanding of what that data represents. As a result, software developers often need to look at what their base classes are, and find the initialization logic of the data based on the call stack to see what the data really means. In this case, Double brace initialization is no longer a high maintenance, but instead becomes the burden of maintenance.

Also, because double brace initialization need to create a derived class of the target type, we cannot use double brace initialization on a type that is final decorated.

It is also worth mentioning that in some Ides, the format of Double brace initialization is actually very strange. This allows double brace initialization to lose its maximum advantage.

And before using double brace initialization, let's first ask ourselves: Are we using a series of constants to initialize a collection? If so, why mix the data with the application logic? If either of these questions is negative, then it means that we should use separate files to record the data that the application needs, such as the *.properties file, and load the data when the app is running.

appropriate use Double Brace Initialization

It can be said that the Double brace initialization, although it has a prominent advantage in ideographic, its shortcomings are very obvious. As a result, software developers need to be cautious about using it.

As we have seen in the previous introduction, the biggest problem with Double brace initialization is the increased maintenance cost when expressing complex data, the ambiguity of semantics in the Equals () function, and the potential for memory leaks.

The first drawback is very easy to avoid, that is, when creating a complex collection of data, we no longer consider using double brace initialization, but instead store the data in a dedicated data file and load it while the app is running.

The other two drawbacks can be accomplished by restricting the scope of the data used in that part.

What should we do when we need to initialize complex data? A range of solutions have been proposed for this industry. These scenarios can not only improve the ideographic nature of the code, but also avoid the series of problems introduced by the use of double brace initialization.

One of the most common solutions is to use a third-party class library. For example, the Arrayutils.tomap () function provided by the Apache Commons Class library provides a very clear implementation of map creation:

1 map<integer, string> map = (map) arrayutils.tomap (new  object[][] {2         {1 , "One"},3         {2, "one"},4         {3, "three"}5 });

If you don't like to introduce a third-party class library, you can do something like this by creating a tool function:

Map<integer, string> map = Utils.tomap (new  object[][] {    {1, "One"},    {2, "one") },    {3, "three"}});  Public Map<integer, string> tomap (object[][] mapData) {    ...}

Reprint please specify the original address and marked reprint: http://www.cnblogs.com/loveis715/p/4593962.html

Commercial reprint please contact me in advance:[email protected]

Java:double Brace Initialization

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.