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

Why does the equals() implementation generated by Eclipse check for null before type checking (instanceof)?

我经常使用 Eclipse 的代码生成工具(Source / Generate hashCode() and equals()...)为简单的 POJO 类 创建 equals() 实现。如果我选择 "Use instanceof to compare types" 这会产生一个类似于此的 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
  }

今天一位同事指出,第二个 if 语句根本没有必要,因为只要 obj 为 null,instanceof 类型检查就会 return false。 (See question 3328138.)

现在,我猜那些为 Eclipse JDT 编写代码模板的人也值得一试。所以我想肯定有一些空检查的原因,但我不确定它是什么?

(另外 question 7570764 可能会给出一个提示:如果我们使用 getClass() 比较而不是 instanceof 进行类型检查,obj.getClass() 不是 null 安全的。也许代码模板不聪明如果我们使用 instanceof 就足以省略 null 检查。)

编辑:Dragan 在他的回答中注意到,instanceof 类型检查不是 Eclipse 中的默认设置,因此我将其编辑为排除了问题。但这并没有改变任何东西。

另外请不要建议我使用 getClass() 或(甚至更好!)不同的 IDE。那不是重点,那不回答问题。我没有征求关于如何编写 equals() 实现、是否使用 instanceof 或 getClass() 等的建议

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

确实没有必要,是Eclipse模板作者的错误。这不是第一个;我在那里发现了更多更小的错误。比如我想省略null值时生成toString()方法:

public class A {
    private Integer a;
    private Integer b;

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("A [");
        if (a != null)
            builder.append("a=").append(a).append(", ");
        if (b != null)
            builder.append("b=").append(b);
        builder.append("]");
        return builder.toString();
    }
}

如果a不是nullb是,则].

前会有一个额外的逗号

所以,关于你的陈述:"Now, I guess that the folks writing the code templates for Eclipse JDT are worth their salt, too.",我认为他们是,但更多地关注这些微小的不一致不会伤害他们。 :)

这是不必要的,因为 instanceof 有一个内置的空检查。 但是 instanceof 远不止简单的 foo == null。这是一个完整的说明,准备 class 检查在空检查完成之前做不必要的工作。 (详见http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof

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

进行 nullcheck 更快

但通常您在 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);
    }
}