16

I have a class with lots of final members which can be instantiated using one of two constructors. The constructors share some code, which is stored in a third constructor.

// SubTypeOne and SubTypeTwo both extend SuperType

public class MyClass {
    private final SomeType one;
    private final SuperType two;


    private MyClass(SomeType commonArg) {
        one = commonArg;
    }

    public MyClass(SomeType commonArg, int intIn) {
        this(commonArg);

        two = new SubTypeOne(intIn);
    }

    public MyClass(SomeType commonArg, String stringIn) {
        this(commonArg);

        two = new SubTypeTwo(stringIn);
    }

The problem is that this code doesn't compile: Variable 'two' might not have been initialized. Someone could possibly call the first constructor from inside MyClass, and then the new object would have no "two" field set.

So what is the preferred way to share code between constructors in this case? Normally I would use a helper method, but the shared code has to be able to set final variables, which can only be done from a constructor.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
Rag
  • 6,405
  • 2
  • 31
  • 38

4 Answers4

19

How about this? (Updated for changed question)

public class MyClass {

    private final SomeType one;
    private final SuperType two;

    public MyClass (SomeType commonArg, int intIn) {
        this(commonArg, new SubTypeOne(intIn));
    }

    public MyClass (SomeType commonArg, String stringIn) {
        this(commonArg, new SubTypeTwo(stringIn));
    }

    private MyClass (SomeType commonArg, SuperType twoIn) {
        one = commonArg;
        two = twoIn;
    }
}
Rag
  • 6,405
  • 2
  • 31
  • 38
Richard Taylor
  • 2,702
  • 2
  • 22
  • 44
  • Ah, that's my fault. I messed up my example a little bit; check out the way I've written it now. However, you've given me the idea to combine both constructors into one that takes SuperType and have the caller actually construct the SuperType object before calling. I'd prefer to not have SuperType visible where the call occurs, but that's the best I can think of. – Rag Feb 15 '13 at 23:53
  • Understandable. How about this? It avoids allowing any subclass of `SuperType`, avoids duplication, and always assigns to both variables. Note, the cast on the `null` to avoid ambiguity. https://gist.github.com/stickyd/4965120 – Richard Taylor Feb 16 '13 at 02:00
6

You need to make sure that in every constructor you are initializing all final variables. What I would do is have one constructor that initializes all the variables and have all the other constructor call that, passing in null or some default value if there is a field that they are not given a value for.

Example:

public class MyClass {
    private final SomeType one;
    private final SuperType two;

    //constructor that initializes all variables
    public MyClas(SomeType _one, SuperType _two) {
        one = _one;
        two = _two;
    }

    private MyClass(SomeType _one) {
        this(_one, null);
    }

    public MyClass(SomeType _one, SubTypeOne _two) {
        this(_one, _two);
    }

    public MyClass(SomeType _one, SubTypeTwo _two) {
        this(_one, _two);
    }
}
Steve K
  • 4,863
  • 2
  • 32
  • 41
scaevity
  • 3,991
  • 13
  • 39
  • 54
1

All you need to do is ensure that "two" gets initialized. In the first constructor, just add:

two = null;

unless there's some other value you'd like to give it in the event that only the first constructor is called.

Halogen
  • 551
  • 6
  • 11
  • You can initialize a final variable to null. It just has to be initialized to *something*. If you want two to have a non-null value, then you have to invoke one of the other constructors. It has the same end result as Sticky's answer. – Halogen Feb 16 '13 at 00:06
  • 2
    But the point is that if I set `two` to null in the first constructor then I can't set its actual value in the other constructors because it has already been set. – Rag Feb 18 '13 at 04:40
0

You get this error because if you had called MyClass(SomeType oneIn), two is not initialized.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186