为啥 Eclipse 生成的 equals() 实现会在类型检查(instanceof)之前检查 null?
Posted
技术标签:
【中文标题】为啥 Eclipse 生成的 equals() 实现会在类型检查(instanceof)之前检查 null?【英文标题】:Why does the equals() implementation generated by Eclipse check for null before type checking (instanceof)?为什么 Eclipse 生成的 equals() 实现会在类型检查(instanceof)之前检查 null? 【发布时间】:2015-11-09 22:48:20 【问题描述】:我经常使用 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
今天一位同事指出,第二个if语句根本不需要,因为只要obj为null,instanceof类型检查就会返回false。 (See question 3328138.)
现在,我想那些为 Eclipse JDT 编写代码模板的人也值得一试。所以我认为空检查一定有一些原因,但我不确定它是什么?
(另外question 7570764 可能会给出提示:如果我们使用 getClass() 比较进行类型检查而不是 instanceof,则 obj.getClass() 不是 null 安全的。也许代码模板不够聪明,无法省略 null 检查如果我们使用 instanceof。)
编辑:Dragan 在他的回答中注意到,instanceof 类型检查不是 Eclipse 中的默认设置,所以我编辑了这个问题。但这不会改变任何事情。
另外请不要建议我应该使用 getClass() 或(甚至更好!)不同的 IDE。这不是重点,这不能回答问题。我没有就如何编写equals()实现、是否使用instanceof或getClass()等征求意见。
问题大致是:这是 Eclipse 中的一个小错误吗?如果不是,那为什么它可以作为一项功能呢?
【问题讨论】:
不知道,但 instanceof 并不总是被认为是最好的方法。另一种方法是将 obj.getClass() 与 this.getClass() 进行比较,然后您确实需要进行 null 检查 我会引用a comment on this answer。Specifically, in Item 8, he notes that in equals() methods, one instanceof operator serves two purposes - it verifies that the argument is both non-null and of the correct type. "...[S]o you don't need a separate null check."
IntelliJ Idea 生成一个包含null
检查的equals
implementation,但使用的是getClass()
:if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; /*...*/
使用 Eclipse Luna。空检查后生成的代码getClass() != obj.getClass()
。
Any reason to prefer getClass() over instanceof when generating .equals() 很好地介绍了是否使用instanceof
或getClass()
。 Josh Bloch 喜欢使用instanceof
;他的论点可以从this (old) interview找到。
【参考方案1】:
这是不必要的,因为 instanceof 有一个内置的空检查。 但是 instanceof 不仅仅是一个简单的 foo == null。这是一个完整的指令,在完成空检查之前准备类检查做不必要的工作。 (详情请参阅http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.instanceof)
因此,单独的 null 检查可能会提高性能。 进行了快速测量,毫不奇怪 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);
【讨论】:
我确信instanceof
指令在内部空值检查之前所做的事情与手动空值检查相比微不足道,尤其是在优化的环境中。
在优化的环境中,我希望“手动”nullcheck 的优化与内部的一样好。 (说汇编程序都会归结为 MOV CMP JNE )
Instanceof 作为指令使用堆栈有一个pop(引用),push(结果)比一个简单的nullcheck。这在现实中会用掉多少很难说,并且可能因操作系统和硬件而异。 (这就是为什么分析是检查性能问题的理想选项)【参考方案2】:
确实,这是不必要的,这是 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
不是null
而b
是,则在结束]
之前会有一个额外的逗号。
所以,关于您的陈述:“现在,我想为 Eclipse JDT 编写代码模板的人也值得他们的盐分。”,我认为他们是,但不会有坏处他们要更加注意这些微小的不一致。 :)
【讨论】:
好的,现在我不确定默认是什么(或曾经是什么),所以我编辑了该部分。以上是关于为啥 Eclipse 生成的 equals() 实现会在类型检查(instanceof)之前检查 null?的主要内容,如果未能解决你的问题,请参考以下文章
JAVA中重写equals方法为啥要重写hashcode方法说明
Eclipse做C程序时为啥Debug下不能生成exe文件,新手在线等帮助,悬赏!
用eclipse生成JAR文件,为啥main class项啥都没有?