Asked  6 Months ago    Answers:  5   Viewed   31 times

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

 Answers

61
  • 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.

Tuesday, June 1, 2021
 
DaveRandom
answered 6 Months ago
78

The simple answer is: yes.


The reason is quite simple as well, if you store by value you might either need to move (from a temporary) or make a copy (from a l-value). Let us examine what happens in both situations, with both ways.

From a temporary

  • if you take the argument by const-ref, the temporary is bound to the const-ref and cannot be moved from again, thus you end up making a (useless) copy.
  • if you take the argument by value, the value is initialized from the temporary (moving), and then you yourself move from the argument, thus no copy is made.

One limitation: a class without an efficient move-constructor (such as std::array<T, N>) because then you did two copies instead of one.

From a l-value (or const temporary, but who would do that...)

  • if you take the argument by const-ref, nothing happens there, and then you copy it (cannot move from it), thus a single copy is made.
  • if you take the argument by value, you copy it in the argument and then move from it, thus a single copy is made.

One limitation: the same... classes for which moving is akin to copying.

So, the simple answer is that in most cases, by using a sink you avoid unnecessary copies (replacing them by moves).

The single limitation is classes for which the move constructor is as expensive (or near as expensive) as the copy constructor; in which case having two moves instead of one copy is "worst". Thankfully, such classes are rare (arrays are one case).

Wednesday, July 28, 2021
 
davidb
answered 4 Months ago
86

You cannot execute statements freely in a class. They should be inside a method. I recommend you to add this line in the constructor of the class or in a class initialization block.

In class constructor:

public class Test {
    // some instance variables...

    private List<String> list = new ArrayList<>();

    public Test() {
        list.add("asdf");
    }
    // methods here...
}

In class initialization block:

public class Test {
    // some instance variables...

    private List<String> list = new ArrayList<>();

    {
        list.add("asdf");
    }
    // methods here...
}

More info:

  • What's the difference between an instance initializer and a constructor?
Friday, August 6, 2021
 
gMale
answered 4 Months ago
78

You should use a global Random instance for performance reasons, instead of initializing one each time - see also API.

Please note that:

  • for multi-threaded applications, you should use a ThreadLocalRandom instead, in the form of ThreadLocalRandom.current().next... for each call (see official recommendation on the matter).
  • for cryptographically secure random numbers, use a SecureRandom instead.
Tuesday, August 24, 2021
 
Bentleylust
answered 3 Months ago
85

I recently discovered ActiveSupport defines class_inheritable_accessor, which does what the class-instance variables do with the advantage that objects are not shared across inheritance, and you can have a default value for the variable when subclassing.

class Foo
  class_inheritable_accessor :x, :y
end

Foo.x = 1

class Bar < Foo
end

Bar.x #=> 1
Bar.x = 3
Bar.x #=> 3
Foo.x #=> 1

More info here

Just for completeness: of the two presented options, I prefer going with the class-instance variables, since is often the expected behavior.

Tuesday, November 23, 2021
 
xenon
answered 7 Days ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share