8

Possible Duplicate:
How is an instance initializer different from a constructor?

When all the required work can be done inside the constructor, then why do we still need non-static block in Java?

EDIT: What about normal classes for which non-static blocks run everytime before the constructor?

0

2 Answers 2

15

In additional to @Bohemian's answer.

If you have multiple constructors an intialiser block avoid duplication.

public class A {
     final Map<String, String> map = new HashMap<String, String>(); {
        // put things in map.
     }
     final int number; {
        int number;
        try {
            number = throwsAnException();
        } catch (Exception e) {
            number = 5;
        }
        this.number = number;
     }

     public A() { /* constructor 1 */ }
     public A(int i) { /* constructor 2 */ }
}

What about normal classes for which non-static blocks run everytime before the constructor?

Technically the order is

  • the super constructor is always called first
  • all the initializer blocks in order of appearence.
  • the constructor code

In reality all this code is in the byte code for each constructor so there is nothing before or after the constructor at runtime.


Before there is any more confusion as to the order of initialization

public class Main extends SuperClass {
    {
        System.out.println("Initialiser block before constructor");
    }

    Main() {
        System.out.println("Main constructor");
    }

    {
        System.out.println("Initialiser block after constructor");

    }

    public static void main(String... args) {
        new Main() {{
            System.out.println("Anonymous initalizer block");
        }};
    }
}

class SuperClass {
    SuperClass() {
        System.out.println("SuperClass constructor");
    }
}

prints

SuperClass constructor
Initialiser block before constructor
Initialiser block after constructor
Main constructor
Anonymous initalizer block
Sign up to request clarification or add additional context in comments.

13 Comments

It looks a bit confusing to place the leading parenthesis on the same line as the declaration of number.
@veer Its a styling issue. Its to show that block relates specifically to that field.
It can by repeating all the code in each constructor or finding a way to use this() The problem is remembering to add the bits before and after if you add a constructor. Its much simpler not to need to do this.
@Ashwyn, good point., fixing.
@PeterLawrey Instance blocks for anonymous classes execute after the constructor, in order of appearance. Maybe distinguish that in your answer from execution order for instance blocks within a class. (ps congrats on 100K, and for your java connector character answer making the newsletter!).
|
8

You can use it with an anonymous class:

new MyClass() {
    {
         // do something extra on construction (after the constructor executes)
    }
}

I find this is particularly useful for initializing "look up" maps (ie fixed contents) in place:

Map<String, String> map = new HashMap<String, String>() {
    {
        put("foo", "bar");
        put("one", "two");
        // etc
    }
};

FYI, this is sometimes (poorly) called "double brace initialization", when in fact it's simply employing an initializer block.

Although such an anonymous class is technically a subclass, the beauty of this is shown when comparing using this technique with a more traditional one in creating an unmodifiable map:

Compare this straightforward one-liner, which places data and assignment together:

private static final Map<String, String> map = Collections.unmodifiableMap(
    new HashMap<String, String>() {{
        put("foo", "bar");
        put("one", "two");
        // etc
    }});

With this mess, which must create a separate object due to final only allowing one assignment:

private static final Map<String, String> map;

static {
    Map<String, String> tempMap = new HashMap<String, String>();
    tempMap.put("foo", "bar");
    tempMap.put("one", "two");
    // etc
    map = Collections.unmodifiableMap(tempMap);
}

Also note that with the mess version, the two statements need not be adjacent, so it can become less obvious what the contents of the unmodifiable map is.

6 Comments

Note that initializing containers like this (known as "double brace initialization") creates a subclass rather inappropriately -- similar to how subclassing Thread is considered inappropriate when you merely want to implement Runnable. Careful when abusing inheritance; one example mentioned there is how it can break a common implementation of equals. You should probably look into using a collection factory method.
@veer Nice link/read. I think it's better to check against the CTT of the class (and/or any supported superclasses) in equals, though ..
@veer I absolutely do not consider this an "abuse of inheritance"; it's simply populating an object in-line. I use this frequently in my code without any problems. See edited answer.
@Bohemian you can simplify your code even further by using a factory method, e.g. Collections.unmodifiableMap(newHashMap<String, String>("foo", "bar", "one", "two", ...)). Aside from that, I think you should look into the Liskov Substitution Principle.
@Bohemian I made an error, Maps.<String, String>newHashMap*. But regardless, it could return a Map<String, String> (though it'd be rather pointless given the method contract). I'm not trying to be a pedant, but one should take care when using inheritance in an unconventional way like that.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.