【问题标题】:How to fix "Constructor Calls Overridable Method"如何修复“构造函数调用可覆盖的方法”
【发布时间】:2012-04-26 15:55:29
【问题描述】:

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

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

谢谢!

【问题讨论】:

  • 您显示的代码在调用可重写方法之后 它被称为构造函数,而不是 from 构造函数,对吗?还是我错过了什么?
  • 这是关于特定 IDE 的吗?因为从编译器的角度来看,我认为这没有任何问题。
  • @user973479 afaik 只有在 doFoo() 是 Bar 本身的方法时才会出现此警告。所以无论是你的例子还是你的 IDE 都是错误的。
  • @user973479 你用的是什么IDE? IntelliJ、javac 和 eclipse 都没有给我一个警告,很明显没有理由让 doFoo 成为最终的 - 只有当我们调用 constructed 实例的非最终实例方法时才有意义。跨度>
  • 我正在使用 SONAR 进行代码覆盖,并收到了该代码的消息。我想也许 SONAR 有错误?

标签: java constructor overriding


【解决方案1】:

正如@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;
    }
}

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

【讨论】:

    【解决方案2】:

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

    public final void doFoo() { }

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 在构造函数中调用一个完全不同的类的非 final 方法有什么潜在的不安全性?
      • 您对我的回答投了反对票,因为您不了解构造函数中可覆盖行为所带来的影响?本文解释了在构造函数中调用覆盖方法的潜在危险:javapractices.com/topic/TopicAction.do?Id=215
      • 如果您真的阅读了我的帖子,您会注意到问题是关于在已经完全构造的对象上调用虚拟方法。在构造对象上调用虚方法的众所周知的缺点是众所周知的,但在这里并不适用。
      • 我可以看到您正在使用具体的实现,但您的 ide 不能;)这就是您看到消息的原因
      • 如果您的 IDE 无法区分对本地方法的调用(只能是以下之一:this.foo()super.foo()foo())与调用实例上的方法,你有一个真正的问题。也没有,javac、intellij 和 eclipse 都做对了,但是 OP 已经指定 Sonar 发出警告 - 显然是 Sonar 中的一个错误。
      【解决方案4】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2013-03-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-08
        • 2016-02-27
        • 2011-08-31
        • 2011-03-25
        • 2014-01-18
        相关资源
        最近更新 更多