24

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?

GlenPeterson
  • 14,920
dirkk
  • 348
  • 3
    The example you've posted doesn't include anything that looks like an initializer block. – Simon B May 12 '14 at 15:01
  • 6
    @SimonBarker look again – the { doStuff(); } on the class level is an initializer block. – amon May 12 '14 at 15:03
  • @SimonBarker The code block that is surrounding doStuff() – dirkk May 12 '14 at 15:03
  • 2
    http://www.ayp-sd.blogspot.de/2012/12/double-brace-initialization-in-java.html – gnat May 12 '14 at 15:13
  • @gnat Thanks, that's a helpful link in explaining a legitimate use case. Also, now I finally made the connection between the double braces and initializer blocks - I always assumed double braces is simply a language construct. – dirkk May 12 '14 at 15:22
  • @amon and dirkk: The original post (probably the one Simon saw) had a typo with the "first constructor/second constructor" comments that made it look like there wasn't one. Granted it would have been a syntax error, but SE's syntax highlighting made the intent harder to spot. – Izkata May 12 '14 at 18:12
  • 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 '14 at 19:37
  • @JAB As these blocks are on class-level I would much more compare them to methods and class variables, which are order-independent. This is the only one on class-level which is (to my knowledge). In fact, I was more think about the difference to languages like Python (where indents and so on are important) - As Java normally is not doing that, I was quite surprised. However, of course the order of LOC is also important in Python and every other non-functional language. – dirkk May 12 '14 at 19:46
  • I also think the comparison to LOCs does not hold as I can easily determine the order of execution. However, this is much more difficult here as they can be scattered all over the class. – dirkk May 12 '14 at 19:48
  • @dirkk Class variable declarations are order-independent, but class variable definitions are not. int x = y + 1; int y = 2; will cause an error when placed in the body of a class ("Cannot reference a field before it is defined"). And while the scattering can be an issue, that's not limited to order-dependent code. Unless you're using a code outliner and some form of quick-linking, determining what code is actually executed in a set of method calls can be difficult when all the methods are scattered throughout the class in an arbitrary manner. – JAB May 12 '14 at 19:48
  • @JAB true. However, this throws a compilation error and is easy to spot. Changing the order of initializer blocks will compile correctly (and I think this makes potentially debugging much more complex) – dirkk May 12 '14 at 19:53
  • @dirkk That's true enough, though it's caused by class variables in Java being assigned default values (numerics -> 0, references -> null, etc.) and you'll have the same issue with such assignments in the body of a constructor. Using final for class variables will give you errors if your initializer blocks are out of order, so if you're that worried about it you can just use them for initializing finals as given in the answers. Personally I don't see much use for more than one initializer block anyway, so I only worry about order within the one block when I do use them. – JAB May 12 '14 at 20:02
  • @amon - You're quite right. The initializer block was cunningly disguised to look like the body of the second constructor. – Simon B May 13 '14 at 07:48

5 Answers5

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");
}
Stephen C
  • 25,178
barjak
  • 1,730
  • 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 '16 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. – Colin P. Hill Jul 03 '17 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.

GlenPeterson
  • 14,920
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);
    }
}
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.

Nico
  • 119
  • 1
  • 1
  • 3
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.

Spotted
  • 1,690
  • I think if you set context = null; in your catch block, that you may be able to declare context as final. – GlenPeterson Jun 27 '16 at 17:32
  • @GlenPeterson I tried but it doesn't compile: The final field context may already have been assigned – Spotted Jun 28 '16 at 05: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; } – GlenPeterson Jun 28 '16 at 13:48