我有一个抽象类(Parent
),它有一个抽象方法(doSetup
),以及一个调用doSetup
方法的成员方法。我需要的是构造中的子类(实现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");
}
}
答案 0 :(得分: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";
}
}
控制行为的解决方案是提供一个最终的不可覆盖的方法,该方法以指定的方式调用可覆盖的受保护方法:
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 :(得分:2)
这是实现常见构造代码的一种方法:
<强> Parent.java 强>
try {
Response response = client.newCall(request).execute();
String rep = response.body().string();
if(rep.length() > 0){
Log.i(TAG, "Got Response");
Log.i(TAG, rep);
}
}catch (Exception e){
e.printStackTrace();
}
<强> Child.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);
}
}
<强> MainApp.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,您将看到默认值构造函数已运行,它将输出“Default Value Goes Here”,因为它是由父级构造函数强制执行的。
答案 2 :(得分:0)
为什么不在父构造函数中包含doSetup()
?为了避免从构造函数中调用可重写方法,您可以稍微改变一下模式:
public abstract class Parent {
final String greeting;
public Parent(String greeting) {
this.greeting = greeting;
doSetup();
}
final void doSetup() {
System.out.println(greeting);
}
}
然后你的每个子类&#39;构造函数需要显式调用超级构造函数,例如:
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
}
}
答案 3 :(得分:0)
正如其他人所说,你不应该在超类的构造函数中调用一个可覆盖的方法。这是为了避免this
to escape并最终产生难以调试的竞争条件,或者只是在从覆盖方法中访问尚未初始化的字段时避免抛出NullPointerException
。
现在,关于这个问题,如果你想为所有的构造者运行公共代码,你需要的是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