Easier Java object initialization
Instead of doing this:
JsonObject obj = new JsonObject();
obj.putString("something","some string");
JsonObject childObj = new JsonObject();
childObj.putString("another thing","another string");
obj.putObject("child",childObj);
...you can do this:
JsonObject obj = new JsonObject() {{
putString("something","some string");
putObject("child", new JsonObject() {{
putString("another thing","another string");
}});
}};
To max out on readability in object initialization in your Java code.
Written by Anders Norås
Related protips
15 Responses
What is the double { for?
The external pair of { } defines an anonymous class extension of JsonObject while the internal pair of { } defines a (non static) code block that gets executed by the class initializer at instantiation time.
You should note that double brace initialization of Java Objects will create a new nested inner class each time it is used.
@rosspb3 Yes, that is correct. This technique exploits that the nested inner class is-a JsonObject. I've never found this to be a problem, even if it will use a bit more memory and that it changes the type.
Nice trick for improved readability. But this will affect my application's performance & memory footprint if I start using it everywhere right?
@sujaykrishna The only difference between the anonymous classes and the top-level classes is that the anonymous classes will hold an implicit reference to the outer class.
This won't manifest itself in performance unless you serialize the class, which will have a minor performance implication.
You'll spend a little more memory, but that is largely nothing to worry about. The same GC-rules apply to anonymous classes as top-level classes.
@mikera I respect your concerns. However, this is not primarily about saving yourself from a little typing. It's about improving readability. Remember that we spend more time reading code than writing it.
If a class shouldn't be inherited from, you should declare it as final. You cannot create an anonymous inner class from a final class. Not accepting derived classes has a lot of implications, much more severe than blocking my little initialization trick. For instance final classes cannot be mocked for testing purposes unless you use JDave, PowerMock or something similar.
Remember that this trick exploits the is-a relationship of object orientation, which is a fundamental part of the Java language. Far from sneaky IMHO. Still, this is different from you're vanilla Java, and I agree that my example might be a little hard to grasp the first time you come across it.
Double Brace initialization is great for readability in my opinion. It allows the eyes to note that an object has been allocated and instantiated, then skip over configuration of the object until it's needed.
This is also a useful technique when initializing Map and List instances. But, having seen many people concerned about performance, I typically reserve its usage to my tests.
Example:
final List<String> list = new ArrayList<String>() {{
add("value1");
add("value2");
}}
final Map<String, String> map = new HashMap<String, String>() {{
put("key1", "value1");
put("key2", "value2");
}}
@wimplash Indeed. Particularly for maps, as for lists I find the Arrays.asList method instead, since it is more compact. E.g.:
Arrays.asList(5, 4, 1, 3, 9, 8, 6, 7, 2, 0)
I use this to create objects in tests, but I don't use it in production, at least long term, for the various reasons people have cited here.
If you want to do this in production code, then I suggest refactoring towards the Builder pattern.
Ah! It's annoying that this will throw a warning when applying it to a Serializable like HashMap or ArrayList:
"The serializable class does not declare a static final serialVersionUID"
this is nice tip for readability. but will not work with final classes. like String,StringBuffer etc.
At first sight I thought it is a magic feature but it´s just an anoynmous inner class with an initialization block. You can add double braces so much as you like. Intersting is the fact that initializers are run before constructors but not before superclass constructors.
Doesn't this have the side effect of binding a reference to the class where the anonymous inner class is defined? It's not normally a problem, but I recall having an issue with a @Component in spring containing an annonymous inner class which got called lazyly (much later) over a collection.