equals方法误用不同类型参数导致的问题

Posted Neo Yang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了equals方法误用不同类型参数导致的问题相关的知识,希望对你有一定的参考价值。

背景

最近重构代码引入了一个问题,原因是由于两个模块都定义常量 ONE。 一个定义是数字类型的1, 另一个模块定义的是“1”。其中一个模块使用了equals方法,将传入的字符串参数与常量ONE比较。
抽取了公共代码后,只保留了数字1的常量定义。由于要改的地方很多,import文件路径采用批量替换的方式处理,替换后代码编译没问题。系统上到生产环境后发现有问题了。

问题分析

Java对象都从Object中继承了equals()方法,用于判断两个对象是否相等。开发过程中可以重写equals方法,实现自己的相等比较逻辑。

由于Object的equals()方法入参是Object, 这就导致你可以将两个不同类型的对象做相等比较,编译器不会报错。但实际开发中,我们基本上不会将两个不同类型对象做相等比较。

编译器放过了这个错误,我们也浑然不觉,待系统部署后运行起来才发现问题。软件开发过程是问题发现得越晚,修复问题的成本越高。好的编程习惯是让编译器尽可能多的发现问题。

下面以一段代码为例说明equals方法容易出问题:

  1. 定义一个表示不同颜色的枚举。
/**
 * 不同颜色枚举定义
 */
public enum EnumColor 
    RED("red"),

    BLUE("blue");

    private String color;

    private EnumColor(String color) 
        this.color = color;
    

    public String getColor() 
        return color;
    

  1. 业务逻辑代码根据传入不同的颜色参数做不同的事情
    public static void handleColor(String color) 
        if (color.equals(EnumColor.RED)) 
            // 处理红色
         else if (color.equals(EnumColor.BLUE)) 
            // 处理蓝色
        
    

这段代码编译没有任何问题,但执行结果不是我们想要的。代码中的equals方法传入的参数是EnumColor枚举,比较结果恒为false.

String的equals方法遇到类型不为String的入参,直接判定为false.


正确代码是如下:取出枚举对应的String变量值,再用于比较。

    public static void handleColor(String color) 
        if (color.equals(EnumColor.RED.getColor())) 
            // 处理红色
         else if (color.equals(EnumColor.BLUE.getColor())) 
            // 处理蓝色
        
    

写代码时一个不留神就出问题了。特别是在大批量重构代码时,合并了一些重复定义的常量,看起来差不多,编译器也没有报错。系统看起来也很正常,执行结果是错的。

如果可以在编译期就能发现这种疏忽,问题就不会留到现网,解决的成本也很低。

一种处理方案

  1. 定义一个相等比较工具类
package com.elon.common.utility;

/**
 * 对象相等比较工具类. 该工具类解决不同类型的对象用equals比较编译不报错, 出问题不容易识别的问题.
 *
 * @author elon
 * @since 1.0
 */
public class EqualCompare<T> 
    /**
     * 字符串相等比较器. 常用的工具类可以定义为常量.
     */
    public static final EqualCompare<String> STRING_EQUAL = new EqualCompare<>();

    /**
     * 对象相等比较.
     *
     * @param a 比较的对象
     * @param b 比较的对象
     * @return true-相等; false-不相等
     */
    public boolean equals(T a, T b) 
        if (a == null) 
            return false;
        

        return a.equals(b);
    


该类通过泛型变量设置了参与比较的参数类型,如果传递的参数类型不对,编译直接报错。说明一下,基本类型不需要用equals比较,基本类型相等比较使用==。

使用上面的工具类,如果参与比较的类型不是字符串。编译器飘红:

以上是关于equals方法误用不同类型参数导致的问题的主要内容,如果未能解决你的问题,请参考以下文章

equals()和==

千万不要误用 java 中的 HashCode 方法

JAVA中Integer的==和equals注意

千万不要误用 java 中的 HashCode 方法

8.2.3 覆写 Equals

方法的重载设计