2

If I know what a field will be initialized to, should I initialize it in the field, constructor, or receive it as a parameter? I am asking about best practices. All three options effectively provide the same result. I am not considering what is happening behind the scenes, because I think it would be insignificant.

This is a general question for curiosity's sake. I do not have a specific problem with this at the moment, although I have in the past. I will use an ArrayList in the following example, because it is a case where you know what it will be initialized to, and if you end up actually wanting to initialize it to another existing ArrayList, there is no harm by preinitializing it. Or is there?

For example, where should I initialize an ArrayList?

Initialized in Field:

import java.util.ArrayList;

public class Initializer {
    private ArrayList<String> arrayList = new ArrayList<String>();

    public void addString(String string) {
        arrayList.add(string);
    }
}

Initialized in Constructor:

import java.util.ArrayList;

public class Initializer {
    private ArrayList<String> arrayList;

    public Initializer() {
        arrayList = new ArrayList<String>();
    }

    public void addString(String string) {
        arrayList.add(string);
    }
}

Initialized as Parameter:

import java.util.ArrayList;

public class Initializer {
    private ArrayList<String> arrayList;

    public Initializer(ArrayList arrayList) {
        this.arrayList = arrayList;
    }

    public void addString(String string) {
        arrayList.add(string);
    }
}
Evorlor
  • 1,440
  • 2
  • 16
  • 22
  • 2
    Can you please tell us more conrete, what the purpose of that class will be? – valenterry Dec 20 '14 at 07:55
  • @valenterry this is a general practice question. I don't have a specific example. Which part would you like me to clarify? I used ArrayList because they must be initialized to a new ArrayList before adding to it (unless you want to point it to a different ArrayList, but that shouldn't matter too much). – Evorlor Dec 20 '14 at 11:25
  • 2
    Then the answer has to be: there is no general best way. It depends on what the class should be used for. It is like asking what kind of christmas present one should buy for his partner. It depends. – valenterry Dec 20 '14 at 14:34
  • Possible duplicate of [Dependency Injection: Field Injection vs Constructor Injection?](http://softwareengineering.stackexchange.com/questions/300706/dependency-injection-field-injection-vs-constructor-injection) – gnat Apr 06 '17 at 10:25
  • 2
    @gnat haha my question was asked first :-p – Evorlor Apr 06 '17 at 13:13
  • https://meta.stackexchange.com/questions/293250/how-can-an-older-question-be-marked-as-a-duplicate-of-a-newer-one – gnat Apr 06 '17 at 13:20
  • @gnat I'm cool with it. Just thought it was funny :) I'm bad at asking questions on this site – Evorlor Apr 06 '17 at 13:21

2 Answers2

7

Initializing as a parameter breaks encapsulation in that the caller can then do with the passed in list what it wants (clear at odd times).

ArrayList<String> list = new ArrayList<String>();
Initializer init = new Initializer(list);
//do various thing
list.clear();
//now the list in init is also empty while init may still expect it to be filled

For the other 2 options it depends on what you want to initialize with:

For example a class that needs a thread-safe queue could use one of several implementations (linked list, circular buffer, etc.) The constructor can decide which one to use based on parameters or which constructor was called and even pass parameters to the constructor of the object.

public Initializer(boolean useLinked) {
    if(useLinked){
        this.arrayList = new LinkedList<String>();
    else 
        this.arrayList = new ArrayList<String>();
}

If the implementation will always be the same then just initialize in the field or initializer block and mark it final.

public class Initializer {
    private final ArrayList<String> arrayList = new ArrayList<String>();
}

This prevents other methods from overwriting it accidentally.

ratchet freak
  • 25,706
  • 2
  • 62
  • 97
  • 1
    But it would only break encapsulation if it really is the same empty list all the time, right? I cannot really see a usecase for this. Else, could you explain this in more detail please? – valenterry Dec 19 '14 at 22:09
  • I'm not initializing in a setter in any of those examples though. Your answer half makes sense, but I'm not entirely understanding. Could you possibly go into detail on each of the three ways shown above? – Evorlor Dec 20 '14 at 00:45
  • What the caller does with the passed in list, is only going to affect you if you use it "as is". When you initialize _using_ the passed in list, but not simply by keeping a reference to it, ie copy the contents or pick and choose what you need, then there is no problem. – Marjan Venema Oct 25 '15 at 10:49
  • By the way, I don't see how encapsulation is broken by a parameter being passed in? Yes, there is a problem in the list being shared by caller and callee. Not sure that is what is meant by "breaking encapsulation" though. Encapsulation certainly isn't broken by implementation details leaking out. The class can still decide to use a different implementation of a list regardless of what is passed in, though it would have to change from a simple assignment to something else to accommodate that. – Marjan Venema Oct 25 '15 at 10:55
0

If I know what a field will be initialized to

If you know that, then chances are very high that either your class won't be very reusable or you make the mistake of assuming that you will know how your class will be used now and in future - while actually missing some possible usecases. I can explain this in more detail if you provide more information about what the class is to do semantically.

Usually it is best to go with Initialization by parameter (assuming that the argument will change and not always be an empty list, as stated above). This design is also called depedency injection. It has quite some advantages. E.g. one of them is a much better testability.

You should also use Interfaces instead of accepting only concrete types. So you should go for:

//(...)
private List<String> someList ;

    public Initializer(List someList ) {
        this.someList = someList ;
    }

And someone can then create a new Initializer by new Initializer(new ArrayList()) OR he can do something like new Initializer(new CopyOnWriteArrayList()) as this will be much more performant in some cases. The user of the class can decide best, what kind of list he wants to use. And you, as the class creater, don't care if it is an ArrayList or CopyOnWriteArrayList right? You just want to have a List-thing where you can add items in the same way.

Tulains Córdova
  • 39,201
  • 12
  • 97
  • 154
valenterry
  • 2,429
  • 16
  • 21