Aanroep van abstracte methoden in een superklasse-constructor

Ik heb een abstracte methode aanroep in de constructor van mijn super. Ik kom de fout tegen "Constructor call moet de eerste verklaring zijn in de Constructor." omdat ik een staat in mijn subklassen moet initialiseren voordat ik de constructor van de super aanroep.

Ik begrijp dat de aanroep van de constructeur de eerste moet zijn .. maar ik krijg hierdoor problemen.

Ik heb een abstracte methode in mijn superklasse die alle subklassen zelf implementeren. Maar de constructor van de subklasse vereist argumenten die moeten worden verwerkt voordat de abstracte methode wordt uitgevoerd.

In mijn code heb ik de abstracte methode in de SuperClass constructor, dus je zult natuurlijk het probleem begrijpen: Super klasse doet:

  1. Verkrijg info1 en info2 met super (i1, i2)
  2. Voert een abstracte methode uit via de subklasse

Maar weinig wist de constructeur van de Superclass dat het ook info3 en info4 nodig had, die in de onderstaande regels worden vermeld de super() - regel.

Ik heb geprobeerd manieren te bedenken voor de constructor van de Superclass om terug te gaan en de informatie uit de subklasse te verzamelen voordat het voert de methode uit, maar ik heb nergens aan gedacht.

De methode kan ook geen argumenten vereisen, omdat de argumenten verschillen voor alle abstracte methoden van de subklassen.

Degenen onder jullie die ervaring hebben, hoe kom ik hier omheen?

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

Maar is nog steeds een beginneling, dus vind het moeilijk om vast te houden aan .. Ik heb het gevoel dat ik al deze problemen had kunnen voorkomen als ik de super() kon gebruiken nadat de constructors van de subklassen zijn verwerkt.

Code op aanvraag:

    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;
}

}

En ik wil hier een einde aan maken door iedereen te bedanken voor het nemen van de tijd om een ​​man te lezen en te helpen. Ik waardeer het echt. Hopelijk struikelen ze wanneer iemand anders een soortgelijk probleem heeft.

Bedankt jongens!

0
Ik begrijp niet waarom je value() in de constructor noemt en negeert het resultaat. Ik vermoed dat er een typfout is, en de echte code is this.value = value (); ???
toegevoegd de auteur user949300, de bron
Zie mijn antwoord hieronder. Omdat het berekenen van de waarde() nauwelijks raketwetenschap is, zou ik volledig in de constructor overgaan. Wanneer ze om getValue() vragen, bereken het dan elke keer.
toegevoegd de auteur user949300, de bron
ik kan 2 opties bedenken - 1. bel de methode niet van binnenconstructeur en bel expliciet buiten. 2. Maak een andere abstracte methode die extra informatie verzamelt en noem deze vóór je abstracte methode. trouwens, waarom kun je de handtekening van je methode niet veranderen?
toegevoegd de auteur aishwarya, de bron
Ik denk dat je je code moet posten
toegevoegd de auteur Gonzalo Garcia Lasurtegui, de bron
Ja dat is correct. zo was ik van plan het te gebruiken
toegevoegd de auteur Dennis Dyallo, de bron

3 antwoord

Het probleem waarmee u wordt geconfronteerd, is dat de subklasse-instantie niet volledig is geconstrueerd wanneer u de abstracte (of enige overbodige) methode vanuit de basisklasse aanroept. De enige manier om er zeker van te zijn dat de objectinstantie volledig is geconstrueerd, is de constructor te hebben voltooid.

Een manier om uw probleem op te lossen, is door de constructors alleen te gebruiken voor de opbouw van de instanties en om (dynamische) initialisatie over te laten aan een methode. In gevallen zoals deze kunt u methoden gebruiken zoals:

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;
    }
}

aanroepcode kan de methoden init() en opruimen() expliciet aanroepen of de aanroepen van init() aan een patroon als in:

public void doSomething() {

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

Binnen je init-methode kun je de abstracte methoden aanroepen en er zeker van zijn dat de volledige objectinstantie correct is geconstrueerd.

2
toegevoegd
@Dennis, ja dat bedoelde ik. Ervan uitgaande dat de aanroep van de abstracte methode van een constructor bedoeld is voor initialisatie van de instantie, kunt u de methode init rechtstreeks aanroepen nadat u de nieuwe instantie hebt gemaakt, of de initialisatie laten uitvoeren bij de eerste openbare oproep naar het object.
toegevoegd de auteur rsp, de bron
Dus je bedoelt dat in plaats van de abstracte methode in de constructor te hebben, ik het op een andere methode heb gezet - een initialisatiemethode, zoals je suggereerde? Hoe kan ik controleren op initialisatie? Of zelfs, hoe zou ik initialiseren?
toegevoegd de auteur Dennis Dyallo, de bron
Bedankt, dit is wat ik deed en het lijkt te werken.
toegevoegd de auteur Dennis Dyallo, de bron

Een alternatief voor de expliciete methode init() die door @rsp wordt gesuggereerd, is om die andere resultaten lui te berekenen. bijv.

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

Als alternatief is het niet dat je berekening voor waarde() veel tijd kost. Bereken het altijd gewoon. 30 jaar geleden zou je al deze dingen in de cache opslaan. Caching creëert fouten met objecten die nul of oud zijn. Of subklassen. :-) Processors zijn nu veel sneller, het is vaak eenvoudiger en soms zelfs sneller om gewoon opnieuw te berekenen.

1
toegevoegd

Als we naar het voorbeeld van de ingediende code kijken, is er geen teken dat de abstracte methode in de basisklasse wordt gebruikt. Ik hoop dat het omwille van de vereenvoudiging is. Anders zou het geen zin hebben om die methode als abstract te definiëren.

Voor het cachen van een waarde in een basisklasse die wordt berekend door een subklasse, moet u geen constructor gebruiken. De methode wordt aangeroepen voordat de constructor van de subklasse de kans krijgt om gegevens daarvoor door te geven, waardoor het effect wordt veroorzaakt dat u hebt waargenomen.

In plaats daarvan zou ik een begeleidende methode definiëren voor de abstracte methode die zou controleren of de waarde in de cache wordt opgeslagen en zo niet de cache. Beschouw dit voorbeeld:

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;
  }
}

Een voorbeeld van een subklasse voor deze 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.
}
0
toegevoegd