23

I recently came across a Java construct I have never seen before and was wondering whether I should use it. It seems to be called initializer blocks.

public class Test {
  public Test() { /* first constructor */ }
  public Test(String s) { /* second constructor */ }

  // Non-static initializer block - copied into every constructor:
  {
    doStuff();
  }
}

The code block will be copied into each constructor, i.e. if you have multiple constructor you do not have to rewrite code.

However, I see three main drawbacks using this syntax:

  1. It is one of the very few cases in Java where the order of your code is important, as you can define multiple code blocks and they will be executed in the order they are written. This seems harmful to me as simply changing the order of code blocks will actually change the code.
  2. I do not really see any benefits by using it. In most cases, the constructors will call each other with some pre-defined values. Even if this is not the case, the code could simply be put into a private method and called from each constructor.
  3. It reduces readability, as you could put the block at the end of the class and the constructor is normally at the beginning of the class. It is quite counter-intuitive to look at a completely different part of a code file if you do not expect that to be necessary.

If my above statements are true, why (and when) was this language construct introduced? Are there any legitimate use cases?

13
  • 3
    The example you've posted doesn't include anything that looks like an initializer block.
    – Simon B
    May 12, 2014 at 15:01
  • 6
    @SimonBarker look again – the { doStuff(); } on the class level is an initializer block.
    – amon
    May 12, 2014 at 15:03
  • @SimonBarker The code block that is surrounding doStuff()
    – dirkk
    May 12, 2014 at 15:03
  • 2
    ayp-sd.blogspot.de/2012/12/…
    – gnat
    May 12, 2014 at 15:13
  • 2
    "[S]imply changing the order of code blocks will actually change the code." And how is that any different from changing the ordering of variable initializers or individual lines of code? If there are no dependencies, then no harm occurs, and if there are dependencies, then placing the dependencies out of order is the same as misordering dependencies for individual lines of code. Just because Java lets you refer to methods and classes before they are defined does not mean that order-dependent code is rare in Java.
    – JAB
    May 12, 2014 at 19:37

5 Answers 5

22

There are two cases where I use initializer blocks.

The first one is for initializing final members. In Java, you can initialize a final member either inline with the declaration, or you can initialize it in the constructor. In a method, it is forbidden to assign to a final member.

This is valid:

final int val = 2;

This is valid too:

final int val;

MyClass() {
    val = 2;
}

This is invalid:

final int val;

MyClass() {
    init();
}

void init() {
    val = 2;  // cannot assign to 'final' field in a method
}

If you have multiple constructors, and if you can't initialize a final member inline (because the initialization logic is too complex), or if the constructors cannot call themselves, then you can either copy/paste the initialization code, or you can use an initializer block.

final int val;
final int squareVal;

MyClass(int v, String s) {
    this.val = v;
    this.s = s;
}

MyClass(Point p, long id) {
    this.val = p.x;
    this.id = id;
}

{
    squareVal = val * val;
}

The other use case I have for initializer blocks is for building small helper data structures. I declare a member, and put values in it right after its declarations in its own initializer block.

private Map<String, String> days = new HashMap<String, String>();
{
    days.put("mon", "monday");
    days.put("tue", "tuesday");
    days.put("wed", "wednesday");
    days.put("thu", "thursday");
    days.put("fri", "friday");
    days.put("sat", "saturday");
    days.put("sun", "sunday");
}
2
  • It's not the method call that is invalid. It's the code inside the init method that is invalid. Only constructors and initalizer blocks can assign to a final member variable, thus the assignment in init will not compile.
    – barjak
    Jun 26, 2016 at 20:49
  • 3
    Your fourth code block does not compile. Initalizer blocks run before all constructors, therefore squareVal = val * val will complain about accessing uninitialized values. Initializer blocks cannot possibly depend on any arguments passed to the constructor. The usual solution I've seen to that sort of problem is to define a single "base" constructor with the complex logic, and to define all other constructors in terms of that one. Most uses of instance initializers, in fact, can be replaced with that pattern. Jul 3, 2017 at 17:27
15

In general, do not use non-static initializer blocks (and maybe avoid static ones too).

Confusing Syntax

Looking at this question, there are 3 answers, yet you fooled 4 people with this syntax. I was one of them and I've been writing Java for 16 years! Clearly, the syntax is potentially error prone! I'd stay away from it.

Telescoping Constructors

For really simple stuff, you can use "telescoping" constructors to avoid this confusion:

public class Test {
    private String something;

    // Default constructor does some things
    public Test() { doStuff(); }

    // Other constructors call the default constructor
    public Test(String s) {
        this(); // Call default constructor
        something = s;
    }
}

Builder Pattern

If you need to doStuff() at the end of each constructor or other sophisticated initialization, perhaps a builder pattern would be best. Josh Bloch lists several reasons why builders are a good idea. Builders take a little time to write, but properly written, they are a joy to use.

public class Test {
    // Value can be final (immutable)
    private final String something;

    // Private constructor.
    private Test(String s) { something = s; }

    // Static method to get a builder
    public static Builder builder() { return new Builder(); }

    // builder class accumulates values until a valid Test object can be created. 
    private static class Builder {
        private String tempSomething;
        public Builder something(String s) {
            tempSomething = s;
            return this;
        }
        // This is our factory method for a Test class.
        public Test build() {
            Test t = new Test(tempSomething);
            // Here we do your extra initialization after the
            // Test class has been created.
            doStuff();
            // Return a valid, potentially immutable Test object.
            return t;
        }
    }
}

// Now you can call:
Test t = Test.builder()
             .setString("Utini!")
             .build();

Static Initializer Loops

I used to use static initializers a lot, but occasionally ran into loops where 2 classes depended on each other's static initializer blocks being called before the class could be fully loaded. This produced a "failed to load class" or similarly vague error message. I had to compare files with the last known working version in source control in order to figure out what the problem was. No fun at all.

Lazy Initialization

Maybe static initializers are good for performance reasons when they work and aren't too confusing. But in general, I'm preferring lazy initialization to static initializers these days. It's clear what they do, I haven't run into a class-loading bug with them yet, and they work in more initialization situations than initializer blocks do.

Data Definition

Instead of static initialization for building data structures, (compare with examples in the other answers), I now use Paguro's immutable data definition helper functions:

private ImMap<String,String> days =
        map(tup("mon", "monday"),
            tup("tue", "tuesday"),
            tup("wed", "wednesday"),
            tup("thu", "thursday"),
            tup("fri", "friday"),
            tup("sat", "saturday"),
            tup("sun", "sunday"));

Conculsion

In the beginning of Java, initializer blocks were the only way to do some things, but now they are confusing, error prone, and in most cases have been replaced by better alternatives (detailed above). It's interesting to know about initializer blocks in case you see them in legacy code, or they come up on a test, but if I were doing code review and I saw one in new code, I'd ask you to justify why none of the above alternatives were suitable before giving your code the thumbs-up.

3

In addition to the initialization of an instance variable that is declared as final (see barjak's answer), I would also mention static initialization block.

You can use them as kind of "static contructor".

That way you can do complex initializations on a static variable a the first time the class is referenced.

Here is an example inspired by barjak's one:

public class dayHelper(){
    private static Map<String, String> days = new HashMap<String, String>();
    static {
        days.put("mon", "monday");
        days.put("tue", "tuesday");
        days.put("wed", "wednesday");
        days.put("thu", "thursday");
        days.put("fri", "friday");
        days.put("sat", "saturday");
        days.put("sun", "sunday");
    }
    public static String getLongName(String shortName){
         return days.get(shortName);
    }
}
0
1

As fas as non-static initializer blocks are concerned, their bare function is to act as a default constructor in anonymous classes. That is basically their only right to exist.

0

I totally agree with statements 1, 2, 3. I also never use block initializers for these reasons and I don't know why it exists in Java.

However, I'm forced to use the static block initializer in one case: when I have to instantiate a static field whose constructor can throws a checked exception.

private static final JAXBContext context = JAXBContext.newInstance(Foo.class); //doesn't compile

But instead you have to do:

private static JAXBContext context;
static {
    try
    {
        context = JAXBContext.newInstance(Foo.class);
    }
    catch (JAXBException e)
    {
        //seriously...
    }
}

I find this idiom very ugly (it also prevents you to mark context as final) but this is the only way supported by Java to initialize such fields.

3
  • I think if you set context = null; in your catch block, that you may be able to declare context as final. Jun 27, 2016 at 17:32
  • @GlenPeterson I tried but it doesn't compile: The final field context may already have been assigned
    – Spotted
    Jun 28, 2016 at 5:26
  • oops! I bet you can make your context final if you introduce a local variable inside the static block: static { JAXBContext tempCtx = null; try { tempCtx = JAXBContext.newInstance(Foo.class); } catch (JAXBException ignored) { ; } context = tempCtx; } Jun 28, 2016 at 13:48

Not the answer you're looking for? Browse other questions tagged or ask your own question.