Should I instantiate instance variables on declaration or in the constructor?
Asked Answered
E

15

254

Is there any advantage for either approach?

Example 1:

class A {
    B b = new B();
}

Example 2:

class A {
    B b;

    A() {
         b = new B();
    }
}
Estray answered 3/1, 2010 at 6:53 Comment(0)
I
293
  • There is no difference - the instance variable initialization is actually put in the constructor(s) by the compiler.
  • The first variant is more readable.
  • You can't have exception handling with the first variant.
  • There is additionally the initialization block, which is as well put in the constructor(s) by the compiler:

    {
        a = new A();
    }
    

Check Sun's explanation and advice

From this tutorial:

Field declarations, however, are not part of any method, so they cannot be executed as statements are. Instead, the Java compiler generates instance-field initialization code automatically and puts it in the constructor or constructors for the class. The initialization code is inserted into a constructor in the order it appears in the source code, which means that a field initializer can use the initial values of fields declared before it.

Additionally, you might want to lazily initialize your field. In cases when initializing a field is an expensive operation, you may initialize it as soon as it is needed:

ExpensiveObject o;

public ExpensiveObject getExpensiveObject() {
    if (o == null) {
        o = new ExpensiveObject();
    }
    return o;
}

And ultimately (as pointed out by Bill), for the sake of dependency management, it is better to avoid using the new operator anywhere within your class. Instead, using Dependency Injection is preferable - i.e. letting someone else (another class/framework) instantiate and inject the dependencies in your class.

Increase answered 3/1, 2010 at 7:27 Comment(6)
@Increase Do object initializations go into the constructor before or after the initialization block?Hypophyge
before, I think. But not sure :)Increase
the first variant is more "readable" that's discussable: if you initialise all your fields in the constructor, you know exactly that when you are reading the code, you have just one place where to search...Cavour
@Increase - Can you please explain why you cant have exception handling with the first variant?Canteen
@Increase I think in the first variant, the exception when invoking new can not be handled within the class itself, rather, it would be thrown and let the user to handle that (or not handle that). The second variant, you can put try {} around the new, and do the exception handling within the class itself.Greenness
"And ultimately (as pointed out by Bill), for the sake of dependency management, it is better to avoid using the new operator anywhere within your class. Instead, using Dependency Injection is preferable". At least you said preferable. This Uncle Bob'ism can cause lots of problems (like factory explosion) if followed zealously. There is nothing wrong with the new operator and not all dependencies need to be injected either, especially if you are happy with sociably testing.Bushhammer
B
39

Another option would be to use Dependency Injection.

class A{
   B b;

   A(B b) {
      this.b = b;
   }
}

This removes the responsibility of creating the B object from the constructor of A. This will make your code more testable and easier to maintain in the long run. The idea is to reduce the coupling between the two classes A and B. A benefit that this gives you is that you can now pass any object that extends B (or implements B if it is an interface) to A's constructor and it will work. One disadvantage is that you give up encapsulation of the B object, so it is exposed to the caller of the A constructor. You'll have to consider if the benefits are worth this trade-off, but in many cases they are.

Bouillabaisse answered 3/1, 2010 at 7:31 Comment(11)
On the other hand, it increases coupling in the sense that now you've made the link between A and B more visible. Previously, the use of B was an internal matter of A, and if it turns out that a better design is not to use B, your suggestion is harder to change.Tamatamable
the coupling is there anyway - A needs B. But instantiating it inside the class means "A needs exactly this B ", while DI allows for a number of differnt B's to be used.Increase
A needs B now in this design, and my point was about if that situation changes.Tamatamable
@jk: If you separate object creation from business logic everywhere - specifically where A is created - by using DI and Factory classes, it isn't hard to change at all. It only needs to change in one place, the Factory that creates A objects. If you're consistent about it, it isn't hard to grasp at all. I think the benefits outweigh the costs. The coupling is reduced, and the overall design is eaier to test and maintain.Bouillabaisse
I don't know what jk is thinking, but as Bozho and Bill commented, DI actually improves this design and creates MORE FLEXIBILITY for changing from B than the use of the new operator.Transported
@BilltheLizard And what makes changing AFactory any different from changing A, in terms of A's dependencies? (Rather than the actual type of the object returned from the factory, because that's a decent reason to use a factory... but then you could do A(){b=BFactory.get();} anyway and avoid the parameter, so hey!) If you want complete flexibility you'd go even further with the dependency injection concept and build custom class loaders that determine which classes to load based on associated property files.Demineralize
@BilltheLizard would you use this idiom even for something simple like List<Integer> intList = new ArrayList<>();? This could be entirely an internal implementation detail. Passing an ArrayList into the constuctor seems exactly the opposite of good encapsulation.Jed
@Jed You'd pass a List (or Collection) to the constructor, not an ArrayList. If you pass a class, you're tying your constructor to that implementation. If you decide to change it later, you have to change it everywhere the constructor is called. Always use the most abstract type you can for method parameters and return types.Bouillabaisse
I think whether to use Injection also heavily depends on whether the logic of "passing" an object is valid in the specific scenario. For example, if a class maintain some internal data, and the data will represent to user in some way, the idea of "passing" the data by the user to the class is not valid. –Greenness
I agree with @JaakkoK. Using DI in cases where there is no need to expose implementation sounds like a bad idea. For instance, if I implement an LRU cache using a map and a list I certainly don't want to expose in my LRU class constructor the map and list.Jeane
@JaakkoK. I think the word you're looking for is "cohesion." Like Bill said, this code decreases coupling but it also increases cohesion. After all, that's what you need, "increased cohesion and decreased coupling."Freund
D
26

I got burned in an interesting way today:

class MyClass extends FooClass {
    String a = null;

    public MyClass() {
        super();     // Superclass calls init();
    }

    @Override
    protected void init() {
        super.init();
        if (something)
            a = getStringYadaYada();
    }
}

See the mistake? It turns out that the a = null initializer gets called after the superclass constructor is called. Since the superclass constructor calls init(), the initialization of a is followed by the a = null initialization.

Dismount answered 20/5, 2015 at 0:8 Comment(3)
The lesson here is never to call overridable functions from the constructor! :) Effective Java, item 17 has a good discussion about it.Disfigure
Excellent point. By initializing at declaration, you lose control of exactly when the variable is initialized. And that can but you in the a$$ (yes, compilers do change their implementations too!).Hebetate
@MohitChugh: Indeed, true as a rock. In fact, nowadays modern Java IDE's like NetBeans (and for sure others too) they throw warnings at you if you call overridable methods from the constructor. And this for the reason Edward Falk has encountered.Padova
C
15

my personal "rule" (hardly ever broken) is to:

  • declare all variables at the start of a block
  • make all variables final unless they cannot be
  • declare one variable per line
  • never initialize a variable where declared
  • only initialize something in a constructor when it needs data from the constructor to do the initialization

So I would have code like:

public class X
{
    public static final int USED_AS_A_CASE_LABEL = 1; // only exception - the compiler makes me
    private static final int A;
    private final int b;
    private int c;

    static 
    { 
        A = 42; 
    }

    {
        b = 7;
    }

    public X(final int val)
    {
        c = val;
    }

    public void foo(final boolean f)
    {
        final int d;
        final int e;

        d = 7;

        // I will eat my own eyes before using ?: - personal taste.
        if(f)
        {
            e = 1;
        }
        else
        {
            e = 2;
        }
    }
}

This way I am always 100% certain where to look for variables declarations (at the start of a block), and their assignments (as soon as it makes sense after the declaration). This winds up potentially being more efficient as well since you never initialize a variable with a value that is not used (for example declare and init vars and then throw an exception before half of those vars needed to have a value). You also do not wind up doing pointless initialization (like int i = 0; and then later on, before "i" is used, do i = 5;.

I value consistency very much, so following this "rule" is something I do all the time, and it makes it much easier to work with the code since you don't have to hunt around to find things.

Your mileage may vary.

Chin answered 3/1, 2010 at 7:50 Comment(9)
it might have been for "never initialize a variable where declared" (though it wasn't me). Or the curly-bracket-on-new-line, which is thought to be a C/C++ idiom. Anyway, one upvote from me to compensate ;)Increase
I'd rather people down vote for a technical reason than an aesthetic one (the { } placement, or the non-required usage of them). If people down vote they should at least say what they think is wrong with the answer... there is nothing technically wrong, and it is the way I have coded in C/C++/Java for the last 20 years (well Java 16) so I am 100% certain that it works :-) (and thanks for counter vote :-)Chin
this is ugly as sin, that's what's wrong with it. It's rather amusing that you would eat your own eyes before using the ternary operator, but prefer multiple static initialization blocks over an OOP appropriate constructor. Your way completely breaks Dependency Injection (at face value, yes the compiler essentially fixes it for you by moving everything to the constructor, but then you're essentially teaching people to rely on compiler magic as opposed to the right thing), is unmaintainble, and brings us back to the awful days of C++. Novice readers, please don't do this.Transported
@VisionarySoftwareSolutions I see one static initialization block. For ternary, it'll get ugly fast when you have multiple statements in the future. I don't use DI, I'd rather rely on compiler magic (which it isn't, it is clearly defined in the language spec) that is debuggable than runtime magic that is unknowable.Chin
Agreed, if you ternary multiple statements You're Doing It Wrong (though I used to). My visceral reaction was predicated by the notion that you can achieve the same results via defining the variables and initializing them in the constructor (what the sugar does anyway) over relying on initializer blocks of either static or non-static varieties. They are provided by the language, yes, but so are labels and goto. They are syntactically hard to look at, and unfriendly to Extract Method and Introduce Parameter Object. DI containers, of course, are just global registries of object graphs.Transported
Actually I meant multiple statements in the ternary. If statements never tend to stay one line for long, which is why I don't like ternaries. Putting them in the constructor is fine, but if you have 2 or more constructors (generally I only have one), then you would be duplicating them. If you violate the "rule" about not initializing where you declare them that would lead to code duplication. Instead of DI I pass variables to constructors... so if the value needed to be set differently, eg. for testing, it would become a parameter instead of a hard coded value.Chin
I don't find them hard to look at at all... to each their own :-)Chin
If you're going to include the rule about making variables that can be final, final. Then you should really have included the bit about making all variables that can be private, private.Hypophyge
@TofuBeer: don't worry about it. Most Java developers tend to be overly pedantic and picky. I'm sure they'd pick on code even if Joshua Bloch wrote it (assuming they didn't know it was him). Personal taste is personal taste; ultimately, neither the CPU nor the JRE care about syntactical style.Workbag
P
7

Example 2 is less flexible. If you add another constructor, you need to remember to instantiate the field in that constructor as well. Just instantiate the field directly, or introduce lazy loading somewhere in a getter.

If instantiation requires more than just a simple new, use an initializer block. This will be run regardless of the constructor used. E.g.

public class A {
    private Properties properties;

    {
        try {
            properties = new Properties();
            properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("file.properties"));
        } catch (IOException e) {
            throw new ConfigurationException("Failed to load properties file.", e); // It's a subclass of RuntimeException.
        }
    }

    // ...

}
Perplexed answered 18/2, 2010 at 15:3 Comment(0)
F
4

Using either dependency injection or lazy initialization is always preferable, as already explained thoroughly in other answers.

When you don't want or can't use those patterns, and for primitive data types, there are three compelling reasons that I can think of why it's preferable to initialize the class attributes outside the constructor:

  1. avoided repetition = if you have more than one constructor, or when you will need to add more, you won't have to repeat the initialization over and over in all the constructors bodies;
  2. improved readability = you can easily tell with a glance which variables will have to be initialized from outside the class;
  3. reduced lines of code = for every initialization done at the declaration there will be a line less in the constructor.
Freemon answered 27/2, 2014 at 18:18 Comment(0)
I
3

I take it is almost just a matter of taste, as long as initialization is simple and doesn't need any logic.

The constructor approach is a bit more fragile if you don't use an initializer block, because if you later on add a second constructor and forget to initialize b there, you'll get a null b only when using that last constructor.

See http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html for more details about initialization in Java (and for explanations on initalizer blocks and other not well known initialization features).

Iniquitous answered 3/1, 2010 at 7:2 Comment(3)
thats why you have DI and @Required :)Shermanshermie
Yes. I was only describing the dufferences between OP's two examples.Iniquitous
Chances are a large number of constructors means you're violating the Single Responsibility Principle and have far bigger problems with your design.Transported
P
2

I've not seen the following in the replies:

A possible advantage of having the initialisation at the time of declaration might be with nowadays IDE's where you can very easily jump to the declaration of a variable (mostly Ctrl-<hover_over_the_variable>-<left_mouse_click>) from anywhere in your code. You then immediately see the value of that variable. Otherwise, you have to "search" for the place where the initialisation is done (mostly: constructor).

This advantage is of course secondary to all other logical reasonings, but for some people that "feature" might be more important.

Padova answered 30/7, 2018 at 9:13 Comment(0)
B
1

Both of the methods are acceptable. Note that in the latter case b=new B() may not get initialized if there is another constructor present. Think of initializer code outside constructor as a common constructor and the code is executed.

Bergman answered 3/1, 2010 at 7:0 Comment(0)
A
1

I think Example 2 is preferable. I think the best practice is to declare outside the constructor and initialize in the constructor.

Anthotaxy answered 3/1, 2010 at 7:1 Comment(0)
E
0

The second is an example of lazy initialization. First one is more simple initialization, they are essentially same.

Eskridge answered 3/1, 2010 at 7:1 Comment(0)
C
0

There is one more subtle reason to initialize outside the constructor that no one has mentioned before (very specific I must say). If you are using UML tools to generate class diagrams from the code (reverse engineering), most of the tools I believe will note the initialization of Example 1 and will transfer it to a diagram (if you prefer it to show the initial values, like I do). They will not take these initial values from Example 2. Again, this is a very specific reason - if you are working with UML tools, but once I learned that, I am trying to take all my default values outside of constructor unless, as was mentioned before, there is an issue of possible exception throwing or complicated logic.

Crustacean answered 9/6, 2015 at 14:15 Comment(0)
B
0

The second option is preferable as allows to use different logic in ctors for class instantiation and use ctors chaining. E.g.

class A {
    int b;

    // secondary ctor
    A(String b) {
         this(Integer.valueOf(b));
    }

    // primary ctor
    A(int b) {
         this.b = b;
    }
}

So the second options is more flexible.

Boreas answered 14/6, 2017 at 14:10 Comment(0)
B
0

It's quite different actually:

The declaration happens before construction. So say if one has initialized the variable (b in this case) at both the places, the constructor's initialization will replace the one done at the class level.

So declare variables at the class level, initialize them in the constructor.

Bridgeboard answered 4/2, 2019 at 1:26 Comment(0)
A
0
    class MyClass extends FooClass {
    String a = null;

    public MyClass() {
        super();     // Superclass calls init();
    }

    @Override
    protected void init() {
        super.init();
        if (something)
            a = getStringYadaYada();
    }
}

Regarding the above,

String a = null;

null init could be avoided since anyway it's the default. However, if you were to need another default value, then, because of the uncontrolled initialization order, I would fix as follow:

class MyClass extends FooClass 
{
    String a;
    {
        if( a==null ) a="my custom default value";
    }
    ...
Allie answered 3/10, 2019 at 12:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.