代码之家  ›  专栏  ›  技术社区  ›  anon

为什么我没有被警告这个问题?

  •  2
  • anon  · 技术社区  · 15 年前

    假设我有两个类定义如下:

    public class A {
    
       public A() {
          foo();
       }
    
       public void foo() {
          System.out.println("A");
       }
    }
    
    public class B extends A {
    
       private String bar;
    
       public B() {
          bar = "bar";
       }
    
       @Override
       public void foo() {
          System.out.println(bar);
       }
    
    }
    

    然后我用以下方式实例化b:

    A test = new B();
    

    那么为什么编译器不能分别警告我在b的foo方法中会有一个空指针呢?这不难检查,有时非常有用。

    6 回复  |  直到 15 年前
        1
  •  7
  •   polygenelubricants    15 年前

    虽然这是一个 设计 错误,不是 文法的 错误。

    这是引自 有效的Java第二版,项目17:设计和文档继承,否则禁止 :

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

    一个顺从的编译器会让它编译得很好,因为它在语言上是合法的。幸运的是,代码分析工具可以用来发现这些设计错误,例如 findbugs :

    ur:从超类的构造函数调用的字段方法的未初始化读取(从超类的构造函数调用了)

    在超类的构造函数中调用此方法。此时,类的字段尚未初始化。

        2
  •  4
  •   cadrian    15 年前

    这是一个经典的发现。不要在构造函数中使用实例方法。

    您可能需要查看PMD,特别是“constructorcallsoverridablemethod”规则。

    http://pmd.sourceforge.net/rules/design.html

        3
  •  3
  •   Jesper    15 年前

    出问题的是:你打电话给 foo 的构造函数中的方法 A 但在您调用它的时候,对象还没有完全构造。的建设者 B 还没有执行,所以 bar 仍然具有默认值 null . 这说明了为什么从构造函数调用非最终实例方法是一个坏主意。

    Java编译器不对此进行警告——它警告一些构造,但是编译器的第一个目的是编译代码,它实际上并不是一个非常复杂的代码分析工具。

    您可以使用静态代码分析工具,比如 FindBugs PMD 在代码中查找这样的问题。

        4
  •  2
  •   Péter Török    15 年前

    父亲Josh Bloch说:“有效的爪哇:

    你不能从Java中的构造函数调用虚拟方法。

    这会导致你观察到的确切问题。打电话的时候 B.foo() ,B尚未初始化,因此 B.bar null .

        5
  •  0
  •   Steven Schlansker    15 年前

    通常,编译器不会试图证明字段的空性。它将对局部变量执行此操作,但更改字段值(即序列化、反射或其他欺骗)的方法太多,无法完成彻底的工作。

    如果可能的话,您应该尝试将字段声明为final,这对这一点和其他许多事情有很大帮助。或者,编写代码来防御空值(即“test”.equals(foo)而不是foo.equals(“test”)或显式空值检查)

        6
  •  0
  •   Michael Borgwardt    15 年前

    检查可能是或不是编程错误的各种“不太难检查”的东西不是编译器的工作(其中有数百种)。

    可以说,一个IDE可能会用一个可配置的警告来标记这一点,但同样的,这样的东西太多了,如果在按需编译期间,IDE必须检查所有这些东西,那么它将大大降低日常工作的速度。

    这确实是静态样式的检查程序(如findbugs)所要做的事情。还有-惊喜!-它对这种情况进行了检查:

    UR: Uninitialized read of field method called from constructor of superclass