如何修复“构造函数调用可覆盖的方法”
我有以下设置,它给我一条消息,指出“构造函数调用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版本。