Should I instantiate instance variables on declara

2018-12-31 09:06发布

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();
    }
}

13条回答
初与友歌
2楼-- · 2018-12-31 09:32

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.

查看更多
栀子花@的思念
3楼-- · 2018-12-31 09:40
  • 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.

查看更多
孤独寂梦人
4楼-- · 2018-12-31 09:40

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.

查看更多
时光乱了年华
5楼-- · 2018-12-31 09:41

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.
查看更多
闭嘴吧你
6楼-- · 2018-12-31 09:41

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.

查看更多
查无此人
7楼-- · 2018-12-31 09:48

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.

查看更多
登录 后发表回答