简体   繁体   中英

Calling abstract methods in a superclass constructor

I have an abstract method call in my super's constructor. I run into the error "Constructor call must be the first statement in the Constructor." because I need to initialize some state in my subclasses before calling the super's constructor.

I understand that the constructor call must be first.. but it's making me have some problems.

I have an abstract method in my superclass that all subclasses implements theirselves. But the subclass constructor requires arguments that must be processed before the runs the abstract method.

In my code, I have the abstract method in the SuperClass constructor, so you will understand of course the problem: Super class does:

  1. Get info1 and info2 using super(i1, i2)
  2. Performs abstract method through the subclass

But little did the Superclass constructor know that it also needed info3 and info4, which are declared on the lines beneath the super()-line.

I've been trying to think of ways for the Superclass constructor to have to go back and gather the information from the subclass before it executes the method, but I haven't thought of anything.

Also the method cannot require arguments, as the arguments are different for all the subclasses's abstract method.

Those of you who are experienced, how do I get around this?

After some Googling, it seems to have to do with this: http://webcache.googleusercontent.co...s-constructor/

But is still a novice, so find it hard to hang on to .. I feel I could've avoided all of this trouble if I could just use the super() after the subclasses constructors have been processed.

Code on request:

    abstract public class Prylar {
      abstract public Integer value();

      private String itemName;
      private Person owner;


      public Prylar(Person thisOwner, String iN){
      owner = thisOwner;
      itemName = iN;
          value = value();  
}



    public class Smycken extends Prylar{
     private int antalStenar;
     private boolean guldEllerSilver;
     private int value;

     public Smycken (Person who, String n, int aS, boolean material){
     super(who,n);

     antalStenar = aS;
     guldEllerSilver = material;
}




public Integer value() {

    if (guldEllerSilver){
        value = 2000;
    }
    else{
        value= 700;}
    value += (500*antalStenar);

    return value;
}

}

And I just want to end this by saying thank you all for taking your time to read and help a guy out. I really appreciate it. Hopefully when someone else has a similar problem, they will stumble upon this.

Thanks guys!

The problem you are facing is that the subclass instance is not constructed completely when you call the abstract (or any overriden) method from the baseclass. The only way to be sure that the object instance is constructed completely is to have finished its constructor.

One way to solve your issue is to use the constructors only for construction of the instances and to leave (dynamic) initialisation to a method. In cases like this you can use methods like:

private boolean initialized = false;

public synchronized boolean init() {
    if (!initialized) {
         // allocate resources, call abstract method(s)
         initialized = true;
    }
    return initialized;
}

public synchronized void cleanup() {
    if (initialized) {
         // free resources, call abstract method(s)
         initialized = false;
    }
}

calling code can call the init() and cleanup() methods explicitly or leave the calls to init() to a pattern as in:

public void doSomething() {

    if (init()) {
        // go!
    }
}

Inside your init method you can call the abstract methods and be sure that the complete object instance is constructed correctly.

An alternative to the explicit init() method suggested by @rsp is to compute those other results lazily. eg

public int getValue() {
   if (value == 0) { // some constant or value that means not-computed yet
      value = blah*foo*bar;
   }
   return value;
}

Alternatively, it's not like your calculation for value() takes much time. Just always recalculate it. 30 years ago, you'd cache all this stuff. Caching creates bugs with objects being null or stale. Or subclasses. :-) Processors are way faster now, it is often simpler, and occasionally even faster, to just recalculate.

Looking at the submitted code example there is no sign that the abstract method is used in the base class. I hope it is for the sake of simplification. Otherwise, it would make no sense to define that method as abstract.

To cache a value in a base class which is calculated by a subclass, you should not use a constructor. The method is called before the subclass's constructor has a chance to pass data for it, causing the effect you observed.

Instead, I would define a companion method for the abstract one which would do the check whether the value is cached and if not do the caching. Consider this example:

public abstract class Base {
  private final String name;
  private BigInteger cachedValue;

  public Base(String name) {
    this.name = name;
  }

  public BigInteger calculate() {
    final BigInteger one = BigInteger.ONE;
    //do the calculation involving `value` call
    return value().multiply(one);
  }

  protected abstract BigInteger doComplexCalculation();

  protected BigInteger value () {
    if (cachedValue == null) {
        this.cachedValue = doComplexCalculation();
    }
    return this.cachedValue;
  }
}

An example of a subclass for this case:

public class SubClass extends Base {
  private int number;

  public SubClass(String name, int number) {
    super(name);
    this.number = number;
  }

  @Override
  protected BigInteger doComplexCalculation() {
    //do calculations and return a result which will be cached by the base class
    return BigInteger.valueOf(number);
  }
  //The cached value can then be accessed by other methods 
  //through the use of protected `value` method call.
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM