如何修复“构造函数调用可覆盖的方法”

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

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

谢谢!

正如@Voo所说,

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

从Effective Java 2nd Edition ,Item 17:inheritance的设计和文档,或者禁止它:

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

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

构造函数必须只调用final或private方法

您可以使用静态工厂方法来解决从Bar class创建对象所需的问题。

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

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

所以,你去拥有界面:

 public interface Foo { void doFoo(); } 

和实施:

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

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

  • 使用接口来定义类private Foo fi的变量而不是private 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; } } 

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

如果以后不需要覆盖该方法,则可以将doFoo声明为final:

public final void doFoo() { }

您的IDE告诉您,因为它可能不安全。 您可以提供任何implimentation或doFoo,并在启动时将所有Bar对象设置为不同的东西。 在大多数情况下,这似乎是一个糟糕的设计选择。

您似乎在构造函数中使用策略模式。 在构造函数中使用策略或任何其他可覆盖的行为是不明智的。 在其他地方使用它。

您看到的错误来源是PMD (在那里搜索“overr”),再次构建示例时,此版本的PMD(4.2.6)不会触发此警告。 Sonar只集成了PMD,Checkstyle和其他工具,并提供了概述。 因此,请检查您使用的Sonar(和PMD)版本。

您可以在声纳中查看: Sonar > Quality Profiles > Search for "overr"应突出显示您正在使用的规则。

在Sonar中,您可以检查您正在使用的PMD版本。 转到Sonar > Configuration > Update Center ,然后查看您正在使用的PMD版本。