十月读书笔记:Effective Java(四)--equals二三事

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/l00149133/article/details/52858141

这一篇主要讲了equals方法的一些问题。
一. 重写equals方法要满足的特性
简单说一下,其实从字面意思上大家也都可以明白,而且这部分其实更偏向于数学,所以不再赘述,仅讲一下笔者遇到的几个有趣的问题。
当你重写equals方法时,必须遵守其通用约定。如下是从Object的规范中拷贝来的约定内容:
equals方法实现了等价关系(equivalence relation):
自反性(Reflexive):对于任何非null的引用x,x.equals(x)都必须返回true。
对称性(Symmetric):对于任何非null的引用x、y,当且仅当y.equals(x)为true时,x.equals(y)必须为true。
传递性(Transitive):对于任何非null的引用x、y、z,如果x.equals(y)为true、y.equals(z)为true,则x.equals(z)返回true。
一致性(Consistent):对于任何非null的引用x、y,只要equals比较操作中使用的信息未被修改,那么多次调用x.equals(y)都会一直返回true,或一直返回false。
非空性(Non-Nullity):对于任何非null的引用x,x.equals(null)必须返回false。

一个有趣的问题是下面这段代码:

// Broken - violates symmetry!  
public final class CaseInsensitiveString {  
    private final String s;  
    public CaseInsensitiveString(String s) {  
        if (s == null)  
            throw new NullPointerException();  
        this.s = s;  
}  
// Broken - violates symmetry!  
@Override   
public boolean equals(Object o) {  
    if (o instanceof CaseInsensitiveString)  
        return s.equalsIgnoreCase(((CaseInsensitiveString) o).s);  
    if (o instanceof String)  // One-way interoperability!  
        return s.equalsIgnoreCase((String) o);  
    return false;  
} 

CaseInsensitiveString 包含一个String变量,你可以通过它的equals方法,来跟一个String变量或者CaseInsensitiveString 对象来比较是否相等,这个比较是忽略大小写的。
乍看起来是没有问题的:

CaseInsensitiveString cis = new CaseInsensitiveString("Polish");  
String s = "polish";  

这里cis.equals(s)是true,但是,哈哈,s.equals(cis)是false,因为String类是不会忽略大小写的,这里就违背了对称性。

总而言之,实现高质量的equals方法有如下诀窍:

  1. 使用==操作符来判断参数是否为该对象的一个引用。如果是,则返回true。这只不过是一种性能优化,当比较操作开销很大时,就值得这么做。
  2. 使用instanceof操作符来判断参数是否是正确的类型。如果不是,则返回false。一般来说,正确的类型就是equals方法所在的类。偶尔情况下,是该类实现的某个接口。如果类使用的接口优化了equals约定,允许在实现该接口的不同类间进行比较,那就使用接口。集合接口Set、List、Map、Map.Entry都有这种特性。
  3. 将参数强制转换为正确的类型。因为之前进行了instanceof测试,这里的强制转换能确保成功
  4. 遍历类中的每个关键域,检查参数中的域与对象本身的域是否匹配。对于不是float和double的基本类型,用==操作符进行比较;对于对象引用,则递归调用equals;对于float域,使用Float.compare方法;对于double域,调用Double.compare方法。对float和double域的特殊处理是有必要的,因为存在Float.NaN、-0.0f,以及类似的double常量,可参考Float.equals方法。
  5. 当你完成equals方法后,问自己三个问题:它是否是对称的、传递的、一致的?
  6. 当重写equals时必须重写hashCode
  7. 在equals方法声明中不要将Object替换为其他类型。为了防止这种情况,应该在每次重写equals时都是用@Override。比如如下:
 public boolean equals(MyClass o){
    ....
} 

你会发现程序没有按照你想象的来执行,原因是你没有重写equals,类用的还是原来的equals方法,你这里只是重载

猜你喜欢

转载自blog.csdn.net/l00149133/article/details/52858141