如何修复“构造函数调用可重写方法”

2022-09-04 00:59:03

我有以下设置,它给我一条消息,指出“构造函数调用可重写方法”。我知道这种情况正在发生,但我的问题是如何修复它,以便代码仍然有效并且消息消失。

public interface Foo{
   void doFoo();
}
public class FooImpl implements Foo{
 @Override{
 public void doFoo(){
    //.. Do important code
 }
}
public class Bar{
  private FooImpl fi;
  public Bar(){
    fi = new FooImpl();
    fi.doFoo(); // The message complains about this line
  }
}

谢谢!


答案 1

正如@Voo所说,

您的问题是关于在已经完全构造的对象上调用虚拟方法。众所周知,在 to 构造对象上调用虚拟方法的缺点是众所周知的,但在这里不适用

来自 Effective Java 2nd Edition, Item 17: 设计和记录以供继承,否则禁止继承:

类还必须遵守一些限制才能允许继承。构造函数不得直接或间接调用可重写的方法。如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此子类中的重写方法将在子类构造函数运行之前调用。如果重写方法依赖于子类构造函数执行的任何初始化,则该方法的行为将不会按预期方式运行。

在对象构造期间调用可重写方法可能会导致使用未初始化的数据,从而导致运行时异常或意外结果。

构造函数必须仅调用最终方法或私有方法

您可以使用静态工厂方法来修复必须从 中创建对象的问题。Bar class

有效的 Java,项目 1:考虑静态工厂方法而不是构造函数

类允许客户端获取自身实例的正常方法是提供公共构造函数。还有另一种技术应该成为每个程序员工具包的一部分。类可以提供公共静态工厂方法,该方法只是返回类的实例的静态方法。

所以,你去有界面:

public interface Foo {
     void doFoo();
}

和实现:

public class FooImpl implements Foo {
   @Override
   public void doFoo() {
   //.. Do important code
   }
}

要使用工厂方法创建类,可以按以下方式工作:

  • 使用接口来定义类的变量,而不是 在具体类型上使用接口是良好封装和松散耦合代码的关键。private Foo fiprivate FooImpl fi

  • 将默认构造函数设为私有,以防止在外部实例化类。

    private Bar() { // 阻止实例化 }

  • 删除所有调用以重写构造函数中存在的方法。

  • 创建静态工厂方法

最后,你得到一个带有工厂方法的类,例如:Bar

public class Bar {
    private Foo fi;

    private Bar() {// Prevents instantiation
        fi = new FooImpl();
    }

    public static Bar createBar() {
        Bar newBar = new Bar();
        newBar.fi.doFoo(); 

        return newBar;
    }
}

我的老板说:“声纳警告是关于症状的,而不是关于疾病的。当你能治疗这种疾病时,这是最好的。


答案 2

您可以将 doFoo 声明为 final,如果您以后不需要重写该方法:

public final void doFoo() { }