buguge - Keep it simple,stupid

知识就是力量,但更重要的,是运用知识的能力why buguge?

导航

【避坑指南】告别equals,这些姿势助你比较两个对象

tag:慎用Object#equals(obj) | 其实,我并不鼓励使用Object#equals | 告别equals,这些姿势助你比较两个对象

 

🍀先来看看两个小测试

  1. 表达式 user.getUserId().equals(userSign.getUserId()) ,当user#userId与userSign#userId类型不一致时,结果是什么?
  2. 表达式 new BigDecimal("0.60").equals(new BigDecimal("0.6")) 的结果是?

 

 

 

🍀anyway,企业应用开发中,我并不鼓励使用equals(Object)

《 阿里巴巴Java开发手册1.1.0》→一、编程规约→(四)OOP规约 →6节中有如下规约:

【强制】Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。

正例: "test".equals(object);

反例: object.equals("test");

说明:推荐使用java.util.Objects#equals (JDK7引入的工具类)

上面规约为我们提供了使用equals规避NPE的避坑指南。

应用程序开发中,我其实并不鼓励使用equals,理由是java.lang.Object及其所有派生类的equals的参数类型是Object,无法保证运行期安全。我们复杂的企业级应用,总不可避免的会涉及到代码的重构,而一旦重构数据的数据类型时,equals的弊端就显而易见了,因为equals在编译期无法检测类型的一致性。

 

例如在语句 if (user.getUserId().equals(userSign.getUserId())) {...} 中,当user和userSign的userId的类型出现不一致,就会致使这个if条件不成立。这个类型不一致,并不影响程序的编译和运行,但这往往会导致业务处理的缺陷。也就是说,这个业务缺陷在编译期和运行时无法被我们发现出来。

 

基于此,在业务代码中,尽量不要使用Object作为方法/函数的参数,也尽量不要调用那些参数类型是Object的方法/函数。例如,上面阿里规范提到的java.util.Objects#equals(Object,Object)。

因此,我们有必要使用其他方式取代equals来判断两个对象的内容是否相同。

 

🍀比较数字类型(java.lang.Number),优先使用 ==  来比较基本类型。

下面是用 != 比较两个Integer对象,IDE给出提示。

IDE提示用equals来比较。但最好的方式是用 != 来比较基本类型。

if (riskCompanyEmployee.getType().intValue() != riskCompanyMap.get(dto.getIdCard()).getType())

 

支付结算系统中,如果金额的类型是BigDecimal,那在比较时,可要注意 new BigDecimal("0.60").equals(new BigDecimal("0.6"))  的结果是false。最佳实践是,涉及BigDecimal金额比较时,可以转换成基本数字来比较。例如元转分后对两个int进行比较。

 

🍀比较字符串,使用String#contentEquals,或String#equalsIgnoreCase

String#contentEquals或String#equalsIgnoreCase这2个方法的参数类型都是明确的字符串。

boolean contentEquals(CharSequence cs);

boolean equalsIgnoreCase(String anotherString);

 

在比较两个明确的字符串的内容时,使用String#contentEquals来代替String#equals。如果涉及到不区分大小写,则使用String#equalsIgnoreCase来代替String#equals。

案例:下面含有equals的代码,IDE会提示你使用String#equalsIgnoreCase。重构后的代码更易读→ if(!captcha.equalsIgnoreCase(checkCode.toString())) 

 同样,比较字符串还可以借助诸如下面apache-common的工具方法。

org.apache.commons.lang3.StringUtils.equals(final CharSequence cs1, final CharSequence cs2)

 当然,对于字符串常量,你依然可以使用 == 来比较。

 

🍀比较枚举直接使用 ==

 我们知道,枚举类中的每个枚举项编译后会被标记为public final static,这限定了每个枚举项实例都是一个常量。同时,从java.lang.Enum重写的equals方法实现可以看到,它也是用==来实现的比较。

# java.lang.Enum 中equals 代码

public final boolean equals(Object other) {
    return this == other;
}

 

🍀不光equals,所有入参是Object的方法,都应该慎用!

hutool-all-4.5.11.jar工具包StrUtil.java里封装了如下isBlankIfStr、isEmptyIfStr工具方法。这些入参是Object的工具方法,在企业应用开发者中,与equals(Object)方法一样,都应该慎用。以StrUtil#isBlankIfStr(Object)来举例,StrUtil.isBlankIfStr(orderNo); 当orderNo是空串时返回true。而当orderNo重构为数字类型后,在orderNo=0的情况下,此方法会返回false。这可能不符合实际的业务判断逻辑。

    public static boolean isBlankIfStr(Object obj) {
        if (null == obj) {
            return true;
        } else if (obj instanceof CharSequence) {
            return isBlank((CharSequence) obj);
        }
        return false;
    }

    public static boolean isEmptyIfStr(Object obj) {
        if (null == obj) {
            return true;
        } else if (obj instanceof CharSequence) {
            return 0 == ((CharSequence) obj).length();
        }
        return false;
    }

 

 

 

▄︻┻┳═一Agenda:

▄︻┻┳═一告别equals,这些姿势助你比较两个对象

▄︻┻┳═一听说,你也一直钟爱着equals。。。 

▄︻┻┳═一用int还是用Integer?

posted on 2023-09-01 17:58  buguge  阅读(37)  评论(0编辑  收藏  举报