UnusedNullCheckInEquals告警:如何正确编写equals方法通过PMD校验
问题背景
自定义继承ArrayList<MyObject>的MyObjectList类时重写了equals方法,初始实现如下:
public class MyObjectList extends ArrayList<MyObject> public boolean equals(Object o) { if (this == o) { return true; } else { return o != null && o.getClass().equals(this.getClass()) && super.equals(o); } }
代码提交后PMD扫描触发优先级3的UnusedNullCheckInEquals告警,提示:Invoke equals() on the object you've already ensured is not null。
新增入参为null直接返回false的分支后校验可以通过,修改后代码为:
public boolean equals(Object o) { if (this == o) { return true; } else if (o == null) { return false; } else { return o.getClass().equals(this.getClass()) && super.equals(o); } }
现在需要找到不新增额外代码行就能通过PMD校验的标准正确实现,最初的单行返回逻辑为:
return o != null && o.getClass().equals(this.getClass()) && super.equals(o);
告警原因
这个告警来自PMD的静态分析检测逻辑:规则会识别equals方法里,经过短路与逻辑确认非空的对象上调用equals()的场景,判定前置的null检查属于冗余逻辑。
初始代码中写的是o.getClass().equals(this.getClass()),调用equals的主体是o.getClass(),PMD的分析逻辑会把这个调用和前面的o != null判断关联,认为你在已经完成判空的对象链路上调用equals,误判存在冗余null检查。
无需加行的实现方案
不用新增任何分支或代码行,只需要调整类比较的调用方向,用当前实例天生非空的Class对象作为equals的调用方即可,修改后的代码逻辑和原实现完全一致,可直接通过PMD校验:
public boolean equals(Object o) { if (this == o) { return true; } else { return o != null && getClass().equals(o.getClass()) && super.equals(o); } }
实现说明
- Java实例方法中
this永远非空,因此getClass()(即this.getClass())返回的Class对象一定不存在空指针风险,在这个对象上调用equals不会被PMD判定为“在已判空对象上调用equals”。 - 逻辑完全符合equals契约:先判断是否是同一引用,再通过短路与判断入参非空、入参类型和当前类完全匹配,最后调用父类ArrayList的equals方法比较集合内元素,和原实现、加null分支的实现逻辑完全等价。
- 如果想减少一次方法调用开销,也可以直接用
==比较Class对象——JVM中同一个类加载器下,同一个类的Class对象是全局单例,==比较是完全可靠的,写法为getClass() == o.getClass(),同样不会触发告警。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

