56

I was wondering if the below code makes any sense, since the compiler warns that "the blank final field objects may not have been initialized". Is there a better way of doing this?

public abstract Test {
  protected final ArrayList<Object> objects;
}

public TestSubA extends Test {

  public TestSubA() {
    objects = new ArrayList<Objects>(20);
    // Other stuff
  }
}

public TestSubB extends Test {

  public TestSubB() {
    objects = new ArrayList<Objects>(100);
    // Other stuff
  }
}
Cantillon
  • 1,588
  • 1
  • 12
  • 23

4 Answers4

54

I would make the field final and force the constructors to pass the value up:

public abstract class Test {
  private final ArrayList<Object> objects;

  protected ArrayList<Object> getObjects() {
    return objects;
  }

  protected Test(ArrayList<Object> objects) {
    this.objects = objects;
  }
}

public class TestSubA extends Test {

  public TestSubA() {
    super(new ArrayList<Object>(20));
    // Other stuff
  }
}

public class TestSubB extends Test {

  public TestSubB() {
    super(new ArrayList<Object>(100));
    // Other stuff
  }
}
Michael Myers
  • 188,989
  • 46
  • 291
  • 292
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • +1 Beat me to it -- had the same answer going, with trivial differences (i.e. using an int parameter and calling super() instead of this()). – MCory Feb 24 '10 at 16:18
  • Thanks. Any specific reason why you made it private instead of protected? Say I want to access it directly in the subclasses. And why you use this() instead of super()? – Cantillon Feb 24 '10 at 16:21
  • @MCory @Lieven I'm guessing this was a quick answer from a smartphone, where it's not easy to get everything right the first time. – Michael Myers Feb 24 '10 at 16:25
  • 3
    @lieven: The "this" was a typo - but I prefer fields to be private pretty much everywhere, and shouldn't be part of your exposed API - which includes what derived classes get to see. – Jon Skeet Feb 24 '10 at 18:50
  • From a bytecode/verifier perspective, `final` means only assigned by the class itself (can be unassigned, assigned multiples times, assigned by a method other than a constructor (not 100% sure about from other instances/static methods)). – Tom Hawtin - tackline Feb 24 '10 at 19:52
  • @Tom: That's very interesting. I wish Java allowed multiple assignments within the constructor... this has caused me to write some ugly code to work around it before now. – Jon Skeet Feb 24 '10 at 20:30
  • *maybe* `getObjects` is redundant? If subclass needs a reference to it, it may introduce a new field and store it. Not sure though. – hkoosha Jan 09 '15 at 18:06
  • Of course, this does not work with anonymus classes and `struct` like classes. – Dávid Horváth Apr 04 '16 at 12:49
  • @DávidHorváth: Well neither of those are abstract classes, as described in the question. It's not clear what scenario you're considering here... you might want to ask a new question. – Jon Skeet Apr 04 '16 at 13:56
  • @JonSkeet: I took your advice: http://stackoverflow.com/questions/36411092/is-there-a-way-to-initialize-final-field-in-anonymus-class – Dávid Horváth Apr 04 '16 at 19:22
8

The problem with initializing the final parameters directly in the constructor of the sub-classes is that you need to do it all in one line since super() must be the first statement of the constructor. So instead, I prefer to make the constructor non-public and make a static build method like this:

public abstract class Test {
  protected final ArrayList<Object> objects;

  protected Test(ArrayList<Object> objects) {
    this.objects = objects;
  }
}

public class TestSubA extends Test {
  public static TestSubA build() {
    ArrayList<Object> objects = new ArrayList<Object>(20);
    objects.put(...);
    // Other stuff
    return new TestSubA(objects);
  }

  private TestSubA(ArrayList<Object> objects) {
    super(objects);
  }
}
qwertzguy
  • 15,699
  • 9
  • 63
  • 66
3

Instantiate the objects in the abstract class constructor and just pass the difference to the that constructor.

ctrlShiftBryan
  • 27,092
  • 26
  • 73
  • 78
1

Generally speaking, it might be better to have a constructor in the base class that always sets the field, and not have a default constructor that doesn't set it. The subclasses can then explicitly pass the parameter in the first line of their constructor using super(value)

Uri
  • 88,451
  • 51
  • 221
  • 321