问题:在构造器里调用可重写的方法有什么问题?
我有一个检票页面的类通过抽象方法的结果去去设置页的标题
public abstract class BasicPage extends WebPage {public BasicPage() {add(new Label("title", getTitle()));}protected abstract String getTitle();}
NetBeans就警告我"Overridable method call in constructor"这个信息,但是这是什么错误鸭?
我能想到的唯一替代方案就将抽象方法的结果传递给子类的父类构造器,但是很多参数就无法读取了
回答一
下面有一个帮助你理解的例子
public class Main {static abstract class A {abstract void foo();A() {System.out.println("Constructing A");foo();}}static class C extends A {C() { System.out.println("Constructing C");}void foo() { System.out.println("Using C"); }}public static void main(String[] args) {C c = new C(); }
}
如果你跑一下上面的代码,就会得到下面的结果
Constructing A
Using C
Constructing C
看到了吗?foo()
使用到了C,在C的构造函数开始跑之前。如果foo()
要求c有一个确定的状态(构造函数已经完成了),那么就会遇到一个C未被定义的状态,就会直接失败了。因此你不能确定A里面可重写的foo()
需要什么,你就会碰到一个警告了。
回答二
下面这个例子揭露了当在父类构造器中调用一个可重写的方法可能出现的逻辑问题
class A {protected int minWeeklySalary;protected int maxWeeklySalary;protected static final int MIN = 1000;protected static final int MAX = 2000;public A() {setSalaryRange();}protected void setSalaryRange() {throw new RuntimeException("not implemented");}public void pr() {System.out.println("minWeeklySalary: " + minWeeklySalary);System.out.println("maxWeeklySalary: " + maxWeeklySalary);}
}class B extends A {private int factor = 1;public B(int _factor) {this.factor = _factor;}@Overrideprotected void setSalaryRange() {this.minWeeklySalary = MIN * this.factor;this.maxWeeklySalary = MAX * this.factor;}
}public static void main(String[] args) {B b = new B(2);b.pr();
}
结果可能是:
minWeeklySalary: 0maxWeeklySalary: 0
这是因为B的构造器第一次调用A的构造器,B里面可重写的方法就会被执行。但是里面执行的方法需要使用到我们实例的变量,但是我们实例的变量还未被初始化(因为A的构造器还没完成执行),因为这个变量的值0,而不是1也没有被定义为2(程序员可能认为是这样的)。如果这种逻辑推导要干十多次的话,那就很难去追踪一个错误了
我希望这对你是有帮助的
文章翻译自Stack Overflow:https://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors