Anonymous code blocks in Java

Viewed 45680

Are there any practical uses of anonymous code blocks in Java?

public static void main(String[] args) {
    // in
    {
        // out
    }
}

Please note that this is not about named blocks, i.e.

name: { 
     if ( /* something */ ) 
         break name;
}

.

10 Answers

Instance initializer block:

class Test {
    // this line of code is executed whenever a new instance of Test is created
    { System.out.println("Instance created!"); }

    public static void main() {
        new Test(); // prints "Instance created!"
        new Test(); // prints "Instance created!"
    }
}

Anonymous initializer block:

class Test {

    class Main {
        public void method() {
            System.out.println("Test method");
        }
    }

    public static void main(String[] args) {
        new Test().new Main() {
            {
                method(); // prints "Test method"
            }
        };

        {
            //=========================================================================
            // which means you can even create a List using double brace
            List<String> list = new ArrayList<>() {
                {
                    add("el1");
                    add("el2");
                }
            };
            System.out.println(list); // prints [el1, el2]
        }

        {
            //==========================================================================
            // you can even create your own methods for your anonymous class and use them
            List<String> list = new ArrayList<String>() {
                private void myCustomMethod(String s1, String s2) {
                    add(s1);
                    add(s2);
                }

                {
                    myCustomMethod("el3", "el4");
                }
            };

            System.out.println(list); // prints [el3, el4]
        }
    }
}

Variable scope restrict:

class Test {
    public static void main() {
        { int i = 20; }
        System.out.println(i); // error
    }
}

I use the anonymous blocks for all the reasons explained in other answers, which boils down to limiting the scope of variables. I also use them to have proper delimitation of pairs of method belonging together.

Consider the following excerpt:

jg.writeStartObject();
{
  jg.writeStringField("fieldName", ((JsonFormFieldDependencyData.FieldLocator) valueOrLocator).getFieldName());
  jg.writeStringField("kind", "field");
}
jg.writeEndObject();

Not only you can see at a glance that the methods are properly paired, but doesn't also kind of look like the output too ?

Just be careful to not abuse it and end up in-lining methods ^^

Describe a task, either with a comment or inherently due to the structure of your code and the identifiers chosen, and then use code blocks to create a hierarchical relationship there where the language itself doesn't enforce one. For example:

public void sendAdminMessage(String msg) throws IOException {
    MessageService service; {
        String senderKey = properties.get("admin-message-server");
        service = MessageService.of(senderKey);
        if (!ms.available()) {
          throw new MessageServiceException("Not available: " + senderKey);
        }
    }

    /* workaround for issue 1298: Stop sending passwords. */ {
        final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
        Matcher m = p.matcher(msg);
        if (m.matches()) msg = m.group(1) + m.group(2);
    }
    ...
}

The above is just some sample code to explain the concept. The first block is 'documented' by what is immediately preceding it: That block serves to initialize the service variable. The second block is documented by a comment. In both cases, the block provide 'scope' for the comment/variable declaration: They explain where that particular process ends. It's an alternative to this much more common style:

public void sendAdminMessage(String msg) throws IOException {
    // START: initialize service
    String senderKey = properties.get("admin-message-server");
    MessageService service = MessageService.of(senderKey);
    if (!ms.available()) {
      throw new MessageServiceException("Not available: " + senderKey);
    }
    // END: initialize service

    // START: workaround for issue 1298: Stop sending passwords.
    final Pattern p = Pattern.compile("^(.*?)\"pass\":.*(\"stamp\".*)$");
    Matcher m = p.matcher(msg);
    if (m.matches()) msg = m.group(1) + m.group(2);
    // END: workaround for issue 1298: Stop sending passwords.

    ...
}

The blocks are better, though: They let you use your editor tooling to navigate more efficiently ('go to end of block'), they scope the local variables used within the block so that they cannot escape, and most of all, they align the concept of containment: You are already familiar, as java programmer, with the concept of containment: for blocks, if blocks, method blocks: They are all expressions of hierarchy in code flow. Containment for code for documentary reasons instead of technical is still containment. Why use a different mechanism? Consistency is useful. Less mental load.

NB: Most likely the best design is to isolate the initialisation of the MessageService object to a separate method. However, this does lead to spaghettification: At some point isolating a simple and easily understood task to a method makes it harder to reason about method structure: By isolating it, you've turned the job of initializing the messageservice into a black box (at least, until you look at the helper method), and to fully read the code in order of how it flows, you need to hop around all over your source files. That's usually the better choice (the alternative is very long methods that are hard to test, or reuse parts of), but there are times when it's not. For example, if your block contains references to a significant number of local variables: If you make a helper method you'd have to pass all those variables. A method is also not control flow and local variable transparent (a helper method cannot break out of the loop from the main method, and a helper method cannot see or modify the local variables from the main method). Sometimes that's an impediment.

Related