我有以下设置,它给我一条消息,指出“构造函数调用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
}
}
谢谢!
答案 0 :(得分:16)
正如@Voo所说,
你的问题是关于在已经调用虚拟方法 完全构造的对象。众所周知的呼唤垮台 构造对象上的虚拟方法是众所周知的,但不是 在这里申请
来自Effective Java 2nd Edition,第17项:继承的设计和文档,或者禁止它:
类必须遵守一些限制以允许 遗产。构造函数不能调用可覆盖的方法, 直接或间接地。如果违反此规则,程序将失败 结果。超类构造函数在子类之前运行 构造函数,因此将调用子类中的重写方法 在子类构造函数运行之前。如果是压倒一切的方法 取决于子类构造函数执行的任何初始化, 该方法不会按预期运行。
在对象构造期间调用可覆盖的方法可能会导致使用未初始化的数据,从而导致运行时异常或意外结果。
构造函数必须只调用最终或私有的方法
您可以使用静态工厂方法来解决从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
,使用具体类型的接口是良好封装和松散耦合代码的关键。
将您的默认构造函数设为私有,以防止您的类在外部实例化。
私人酒吧(){ //防止实例化 }
删除所有调用以覆盖构造函数中存在的方法。
创建静态工厂方法
最后,您将获得一个类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;
}
}
我的老板说:“声纳警告是关于症状,而不是疾病。你最好能治疗这种疾病。“!
答案 1 :(得分:6)
如果以后不需要覆盖该方法,则可以将doFoo声明为final:
public final void doFoo() {
}
答案 2 :(得分:0)
您的IDE告诉您,因为它可能不安全。您可以提供任何implimentation或doFoo,并在启动时将所有Bar对象设置为不同的东西。在大多数情况下,这似乎是一个糟糕的设计选择。
您似乎在构造函数中使用策略模式。在构造函数中使用策略或任何其他可覆盖的行为是不明智的。在其他地方使用它。
答案 3 :(得分:0)
您看到的错误来源是PMD(在那里搜索“overr”),再次构建示例时,此版本的PMD(4.2.6)不会触发此警告。 Sonar只是集成了PMD,Checkstyle和其他工具,并提供了概述。因此,请检查您正在使用的Sonar(和PMD)版本。
您可以在声纳中查看:Sonar > Quality Profiles > Search for "overr"
应突出显示您正在使用的规则。
在Sonar中,您可以查看您正在使用的PMD版本。转到Sonar > Configuration > Update Center
,然后查看您正在使用的PMD版本。