2016-04-11 4 views
2

У меня есть абстрактный класс (Parent), который имеет абстрактный метод (doSetup) и метод члена, который вызывает метод doSetup. Мне нужен класс child (который реализует Parent) при построении должен автоматически вызывать метод doSetup, независимо от того, сколько конструкторов может иметь дочерний класс. Есть ли механизм Java или шаблон проектирования, который мог бы помочь мне решить эту проблему?Как автоматически включить выполнение метода родителя во всех дочерних конструкторах?

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

Просто вызовите метод doSetup() в конструкторе базового класса s. И если вы хотите включить функциональность базового класса для определенного метода в дальнейшем, просто используйте 'super.methodYouWishToCall()'. – ManoDestra

+0

То, что я пытаюсь получить, - это автоматическое включение, поэтому, когда другие разработчики реализуют «Parent», у них не будет возможности забыть включить вызов 'doSetup' в свои конструкторы. – yamori

+2

Как я уже сказал, применяйте это в конструкторах базового класса. Затем, когда создаются какие-либо подклассы, необходимо вызвать конструктор базового класса, обеспечивающий выполнение этого метода для вызова. Я могу написать это как ответ для вас, если вы не знаете, что я имею в виду здесь. – ManoDestra

ответ

0

Почему бы не включить doSetup() в родительский конструктор? Для того, чтобы избежать вызова в переписываемый метод из конструктора, вы можете изменить ваш шаблон немного:

public abstract class Parent { 
    final String greeting; 

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

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

Тогда каждый из конструкторов ваших подклассов нужно явно вызвать супер конструктор, например:

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

Хорошая среда IDE, вероятно, будет предостерегать от использования переопределяемых методов в конструкторе.

Причина может быть продемонстрирована с помощью следующего кода, который может иметь неожиданные результаты.

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

Решения для управления поведением было бы предложить один заключительные без Overridable метод, который вызывает в Overridable защищенного метод определенным образом:

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

Есть еще один аспект. Вызов 'init()' в конструкторе гарантирует, что он будет вызван при построении, но позволяя ему переопределить, открывает возможность того, что подкласс переопределяет его реализацией, которая не вызывает 'super.init()', поэтому его вызов больше не применяется ... – Holger

+0

Этот ответ убедил меня не вызывать переопределяемый метод в конструкторе. Для тех, кто задается вопросом, первый пример выше показывает, что порядок построения/создания экземпляра получает метод overriden, но значения переменных экземпляра «Child's» не установлены вовремя для вызова конструктора 'init()' – yamori

+0

@Joop Eggen, просто подтверждая, что вы не имели двойного отрицательного значения в своем названии, следует читать «... предостерегать от использования переопределяемых методов ...» против «... предостерегать от использования переопределяемых методов ...» – yamori

2

Это один из способов реализации общих строительство код:

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

Запустите MainApp выше, и вы увидите, что значение по умолчанию конструктор по запускается и он выведет «по умолчанию Value Goes Here ", так как он выполняется конструктором родителя.

0

Как уже было сказано, вы не должны вызывать переопределяемый метод внутри конструктора суперкласса. Это делается для того, чтобы избежать this to escape и в конечном итоге производить труднодоступные условия гонки или просто избегать NullPointerException s, которые будут выбрасываться при доступе к еще неинициализированному полю из-за чрезмерно сложных методов.

Теперь, что касается вопроса, если вы хотите запустить общий код для всех contructors, что вам нужно, это 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"); 
    } 
} 

Выполнение этого теста:

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

продуцирует следующий результат:

hi 
hi 
another constructor 
Смежные вопросы