为什么Eclipse生成的equals()实现在类型检查(instanceof)之前检查null?

Att*_*pak 14 java eclipse-jdt

我经常使用Eclipse的代码生成工具(Source/Generate hashCode()和equals()...)来为简单的POJO类创建equals()实现.如果我选择"使用instanceof来比较类型",则会生成类似于此的equals()实现:

  @Override
  public boolean equals(Object obj) {
      if (this == obj) {
          return true;
      }
      if (obj == null) {
          return false;
      }
      if (!(obj instanceof MyClass)) {
          return false;
      }
      MyClass other = (MyClass) obj;
      // check the relevant fields for equality
  }
Run Code Online (Sandbox Code Playgroud)

今天一位同事指出,第二个if语句根本不是必需的,因为只要obj为null,类型检查的instanceof就会返回false.(见问题3328138.)

现在,我想那些为Eclipse JDT编写代码模板的人也值得他们的盐.所以我认为这个空检查必须有一些原因,但我不确定它是什么?

(还有问题7570764可能会给出一个提示:如果我们使用getClass()比较进行类型检查而不是instanceof,obj.getClass()不是null安全.如果我们使用instanceof,可能代码模板不够聪明,不能忽略null检查.)

编辑:Dragan在他的回答中注意到,类型检查的instanceof不是Eclipse中的默认设置,所以我编辑了这个问题.但这并没有改变任何事情.

另外请不要建议我使用getClass()或(甚至更好!)一个不同的IDE.这不是重点,不能回答这个问题.我没有询问有关如何编写equals()实现的建议,无论是使用instanceof还是getClass()等.

问题大致是:这是Eclipse中的一个小错误吗?如果不是,那为什么它有资格成为一个功能?

小智 4

这是不必要的,因为instanceof 有一个内置的空检查。但instanceof不仅仅是简单的foo == null。这是一个完整的指令,准备类检查,在空检查完成之前做不必要的工作。(更多详细信息请参见http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof

因此,单独的空检查可能会提高性能。进行了快速测量,毫不奇怪 foo==null 比使用 instanceof 进行 null 检查更快。

但通常在 equals() 中不会有大量的空值,大多数情况下都会进行重复的、不必要的空值检查……这可能会耗尽空值比较期间所做的任何改进。

我的结论是:没有必要。

用于测试完整性的代码(记住使用-Djava.compiler=NONE,否则你只能衡量java的功能):

public class InstanceOfTest {
    public static void main(String[] args) {
        Object nullObject = null;

        long start = System.nanoTime();         
        for(int i = Integer.MAX_VALUE; i > 0; i--) {
            if (nullObject instanceof InstanceOfTest) {}
        }
        long timeused = System.nanoTime() - start;  

        long start2 = System.nanoTime();
        for(int i = Integer.MAX_VALUE; i > 0; i--) {
            if (nullObject == null) {}
        }
        long timeused2 = System.nanoTime() - start2;

        System.out.println("instanceof");
        System.out.println(timeused);       
        System.out.println("nullcheck");
        System.out.println(timeused2);
    }
}
Run Code Online (Sandbox Code Playgroud)