2016-04-11 3 views
2

Ich habe eine abstrakte Klasse (Parent), die eine abstrakte Methode (doSetup) hat, und eine Member-Methode, die die doSetup Methode aufruft. Was ich brauche, ist die Kind-Klasse (die implementiert Parent) bei der Konstruktion sollte automatisch die doSetup-Methode aufrufen, unabhängig davon, wie viele Konstruktoren die Kind-Klasse haben könnte. Gibt es einen Java-Mechanismus oder ein Entwurfsmuster, das mir helfen könnte, dies zu lösen?Wie wird automatisch die Ausführung der Methode eines Elterns in allen Kindkonstruktoren eingeschlossen?

public abstract class Parent { 
    abstract protected void sayHi(); 
    protected void doSetup() { 
    sayHi(); 
    } 
} 

public class Child1 extends Parent { 
    @Override 
    protected void sayHi() { 
    System.out.println("hi"); 
    } 
    public Child1() { 
    // Construction needs to automatically include exec of doSetup() 
    } 
    public Child1(String string) { 
    // Construction needs to automatically include exec of doSetup() 
    System.out.println("another constructor"); 
    } 
} 
+1

Rufen Sie einfach die doSetup() -Methode im Konstruktor Ihrer Basisklasse auf s. Und wenn Sie anschließend die Funktionalität der Basisklasse für eine bestimmte Methode verwenden möchten, verwenden Sie einfach 'super.methodYouWishToCall()'. – ManoDestra

+0

Was ich versuche zu bekommen, ist die automatische Einfügung. Wenn andere Entwickler 'Parent' implementieren, haben sie nicht die Möglichkeit zu vergessen, den 'doSetup' Aufruf in ihre Konstruktoren aufzunehmen. – yamori

+2

Wie gesagt, erzwinge das in den Konstruktoren deiner Basisklasse. Wenn dann alle Unterklassen instanziiert werden, muss der Konstruktor der Basisklasse aufgerufen werden, um den Aufruf dieser Methode zu erzwingen. Ich kann es als Antwort für dich aufschreiben, wenn du dir nicht sicher bist, was ich hier meine. – ManoDestra

Antwort

0

Warum nicht doSetup() in den übergeordneten Konstruktor einbeziehen? Um zu vermeiden, eine überschreibbar Methode aus dem Konstruktor aufrufen, können Sie Ihr Muster ein wenig ändern:

public abstract class Parent { 
    final String greeting; 

    public Parent(String greeting) { 
    this.greeting = greeting; 
    doSetup(); 
    } 

    final void doSetup() { 
    System.out.println(greeting); 
    } 
} 

Dann jedes Ihrer Subklassen Konstrukteurs müssen explizit den Super Konstruktor aufrufen, zum Beispiel:

public class Child1 extends Parent { 
    private static String default_greeting = "hi"; 

    public Child1() { 
    super(default_greeting); // prints "hi" 
    } 

    public Child1(String string) { 
    super(string); // print a different greeting 
    } 
} 
2

Eine gute IDE warnt wahrscheinlich davor, überschreibbare Methoden im Konstruktor zu verwenden.

Der Grund kann mit dem folgenden Code demonstriert werden, der vielleicht überraschende Ergebnisse hat.

class Base { 
    Base() { 
     init(); 
    } 

    protected void init() { 
    } 
} 
class Child extends base { 
    String a = "a"; 
    String b; 
    String c = "c"; 
    String d; 

    public Child() { 
     // 1. Fields are nulled 
     // 2. super() called 
     // 2.1. init() called 
     // 3. Field initialisations done (a, c) 
     // 4. Rest of constructor: 
     System.out.printf("EndConstr a: %s, b: %s, c: %s%n", a, b, c); 
    } 

    @Overridable 
    protected void init() { 
     System.out.printf("Init a: %s, b: %s, c: %s%n", a, b, c); 
     c = "cc"; 
     d = "dd"; 
    } 
} 

Eine Lösung Verhalten zu kontrollieren wäre eine letzte nicht-überschreibbare Methode zu bieten, die eine überwindbare geschützte Methode in einer bestimmten Art und Weise ruft:

class Base { 
    public final void f() { 
     X x = ...; 
     onF(x); 
    } 
    protected /*abstract*/ void onF(X x) { 
    } 
} 
class Child extends Base { 
    @Overridable 
    protected void onF(X x) { 
     ... 
    } 
} 
+1

Es gibt noch einen anderen Aspekt. Der Aufruf von 'init()' im Konstruktor stellt sicher, dass es bei der Konstruktion aufgerufen wird, aber das Überschreiben erlaubt die Möglichkeit, dass eine Unterklasse es mit einer Implementierung überschreibt, die 'super.init()' nicht aufruft Der Aufruf wird nicht mehr erzwungen ... – Holger

+0

Diese Antwort hat mich davon überzeugt, im Konstruktor keine überschreibbare Methode aufzurufen. Für diejenigen, die sich wundern, zeigt das erste Beispiel oben, dass die Reihenfolge der Konstruktion/Instanziierung die überschriebene Methode erhält, aber 'Child'' Instanzvariablenwerte werden nicht rechtzeitig für den Aufruf des Konstruktors an' init() ' – yamori

+0

@Joop Eggen gesetzt, Wenn Sie nur bestätigen, dass Sie in Ihrem Titel kein doppeltes Negativ haben wollten, sollten Sie lesen: "... warnen Sie davor, übersteuerbare Methoden zu verwenden ..." oder "... warnen Sie davor, keine überschreibbaren Methoden zu verwenden ..." – yamori

2

Dies ist eine Möglichkeit, gemeinsam zu implementieren Bauordnung:

Parent.java

public abstract class Parent { 
    public Parent() { 
     this("Default Value Goes Here"); 
    } 

    // Funneling everything through this main constructor. 
    public Parent(String value) { 
     this.doSetup(value); 
    } 

    // I've made this method private, as it shouldn't really 
    // be accessed from sub classes, but if you require that, then 
    // mark this method as protected & final instead. 
    private void doSetup(String value) { 
     System.out.println(value); 
    } 
} 

Child.java

public class Child extends Parent { 
    // Deliberately not implementing constructors here, 
    // but if I did, the first call would be to a super() 
    // constructor to retain parent's construction functionality. 
} 

MainApp.java

public class MainApp { 
    public static void main(String[] args) { 
     Child child = new Child(); 
    } 
} 

Führen Sie das MainApp oben und Sie werden sehen, dass der Standardwert Konstruktor ausgeführt wird, und es gibt „Standard Value Goes Here ", wie es vom Erbauer der Eltern erzwungen wird.

0

Wie andere bereits gesagt haben, sollten Sie keine überschreibbare Methode innerhalb des Konstruktors einer Superklasse aufrufen. Dies soll this to escape vermeiden und schließlich schwer zu debuggende Race-Bedingungen erzeugen oder einfach NullPointerException s vermeiden, die beim Zugriff auf ein noch nicht initialisiertes Feld innerhalb von überschriebenen Methoden ausgelöst werden.

nun im Hinblick auf die Frage, ob Sie gemeinsamen Code für alle contructors ausführen möchten, was Sie brauchen, ist ein initializer block:

public abstract class Parent { 
    { 
     // this is an initializer block that is inherited 
     // by all subclasses and runs for every constructor 
     // in the hierarchy 
     this.doSetup(); 
    } 

    protected final void sayHi() { // final to avoid this to escape 
     System.out.println("hi"); 
    } 

    protected final void doSetup() { // final to avoid this to escape 
     sayHi(); 
    } 
} 

public class Child1 extends Parent { 

    public Child1() { 
     // initilizer block automatically called 
    } 

    public Child1(String string) { 
     // initilizer block automatically called 
     System.out.println("another constructor"); 
    } 
} 

Ausführen dieser Test:

new Child1(); 
new Child1("something"); 

Erzeugt die folgende Ausgabe:

hi 
hi 
another constructor 
Verwandte Themen