5

I have the follow classes:

public abstract class MyAbstractClass {
     protected int x;
     protected int number;
     public MyAbstractClass(int x) {
         this.x = x;
         this.number = this.generateNumber();
     }
     public abstract int generateNumber(); // This class is called in the super constructor and elsewhere
}
public class MySubClass extends MyAbstractClass {
     private int y;
     public MySubClass(int x, int y) {
          super(x);
          this.y = y;
     }
     @Override
     public int generateNumber() {
         return this.x + this.y; // this.y has not been initialized!
     }
}

My issue is that MySubClass's y property has to be initialized before the super constructor is run, because a method using y is run in the super constructor. I am aware that this may not be possible even with some sneaky workaround, but I am yet to find an alternative solution.

Also please keep in mind that I will have many more derived classes, with different values being passed into their constructors.

ouflak
  • 2,458
  • 10
  • 44
  • 49
klo
  • 85
  • 4
  • 1
    Yes, you found a fundamental problem with this aproach. Yes, you should definitely look for an alternative solution. This can mean that you need to design your entire class hierarchy differently, or perhaps you can draw out the problematic part of the construction into some kind of factory. – Hulk Sep 29 '20 at 05:40
  • @Hullk That question does describe my specific issue, but I didn't see any suggestions or workaround there. My goal right now is to find another solution that can do something similar. – klo Sep 29 '20 at 05:44
  • There are several ways to handle this. One way is to separate the calculation of the field values to be used (can be done by a factory) and construction (done by constructor). Construction focusses on enforcing constraints (the classes invariants), without doing too much work itself. – Hulk Sep 29 '20 at 05:48
  • It currently seems like a factory method is the best way to go for my problem, but please let me know if you have any other solutions. – klo Sep 29 '20 at 06:24

1 Answers1

4

You can defer the number calculation until it is needed.

public abstract class MyAbstractClass {
    protected int x;
    protected Integer number;

    public MyAbstractClass(int x) {
        this.x = x;
    }

    public int getNumber() {
        if (number == null) {
            number = generateNumber();
        }
        return number.intValue();
    }

    protected abstract int generateNumber(); 
}
René Link
  • 48,224
  • 13
  • 108
  • 140
  • This is a great solution, but I may have been unclear in my question - the `number` has to be generated in the constructor, just because of how my program is built. It will need to use that value later on in the super constructor. – klo Sep 29 '20 at 05:55
  • @klo then it should be calculated *before* the constructor call. You can also pass the calculation strategy as a parameter to the constructor (depending on how complex it is, as an object of a custom class or as a function interface), instead of having it as an overridable method (along the lines of the principle "prefer composition over inheritance") – Hulk Sep 29 '20 at 06:06
  • @Hulk I can't pass the calculation as a function interface/lambda because it uses properties that would only exist in the subclass. – klo Sep 29 '20 at 06:24
  • @klo you can still pass a lambda, it just has to accept a parameter – Hulk Sep 29 '20 at 16:25
  • Well the lambda type wouldn't be specific, seeing as depending on the derived class, the properties it needs change. – klo Sep 30 '20 at 07:05