Effective Java(二) 对于所有对象都通用的方法

来源:互联网 发布:ubuntu划分区备份 编辑:程序博客网 时间:2024/05/16 16:05

本章将讲述何时以及如何覆盖这些非final类的Object方法(虽然Comparable.compareTo不是Object方法,因为类似也会涉及)

第8条 覆盖equals时请遵守通用约定

什么时候应该覆盖equals方法呢?
如果类具有自己特有的“逻辑相等”概念(不等同于对象等同),而且超类没有覆盖equals方法以实现期望的行为,这时应该覆盖equals方法。
有一种“值类”不需要覆盖equals方法,即用实例受控确保“每个值至多只存在一个对象”,枚举类型就属于这种。

equals方法的通用约定
自反性: 对于非null x,x.equals(x)
对称性: y.equals(x)—->x.equals(y)

传递性:x.equals(y), y.equals(z)—–>x.equals(z)
一致性:只要x和y的equals参数没有修改,x.equals(y)多次调用,返回值都一致。对于不可变对象,相等的对象永远相等,不相等的对象永远不相等。
“非空性”:对于任何非null的引用值x, x.equals(null) 必须返回false
所有的集合类都依赖于传递给它们的对象遵守equals约定

自反性的反例:

public final class CaseInsensitiveString{    private final String s;    public CaseInsensitiveString(String s){        if(s==null){            throw new NullPointerException();            this.s = s;        }    }    @Override    public boolean equals(Object o){        if(o instanceof CaseInsensitiveString){            return s.equalsIgnoreCase((CaseInsensitiveString)o)        }        if(o instanceof String){            return s.equalsIgnoreCase((String)o);        }        return false;    }    public static void main(String[] args){        CaseInsensitiveString cis = new CaseInsensitiveString("Polish");        String s = "polish";        cis.equals(s);//返回true        s.equals(cis);//返回false    }}

传递性的反例:

public class Point{    private final int x;    private final int y;    public Point(int x, int y){        this.x = x;        this.y = y;    }    @Override    pubic boolean equals(Object o){        if(!(o instanceof Point)){            return false;        }        Point p = (Point)o;        return p.x == x && p.y == y;    }}public class ColorPoint extends Point{    private final Color color;    public ColorPoint(int x, int y, Color color){        super(x, y);        this.color = color;    }    @Override    public boolean equals(Object o){        if(!(o instanceof Point)){            return false;        }        if(!(o instanceof ColorPoint)){            return o.equals(this);        }        return super.equals(o) && ((ColorPoint)o).color = color;    }    public static void main(String[] args){        ColorPoint p1 = new ColorPoint(1,2,Color.RED);        Point p2 = new Point(1,2);        ColorPoint p3 = new ColorPoint(1,2,Color.BLUE);        p1.equals(p2)//返回true        p2.equals(p3)//返回true        p1.equals(p3)//返回false    }}

我们无法在扩展可实例化的类的同时,既增加新的值组件,同时又保留equals约定,除非愿意放弃面向对象的抽象所带来的优势。
如果在equals方法中使用getClass代替instanceof,可以扩展可实例化的类和增加新的值组件,同时保留equals约定:

@Overridepublic boolean equals(Object o){    if(o==null||o.getClass()!=getClass()){        return false;    }    Point p = (Point)o;    return p.x == x && p.y == y;}

这种方法只有当对象具有相同实现时,才能使对象等同,但是这在使用多态时可能会出现问题。例如HashSet的contains方法针对ColorPoint将永远返回false.
权宜之计
在ColorPoint中加入一个私有的Point域以及一个公有的视图方法:

public class ColorPoint{    private final Point point;    private final Color color;    public ColorPoint(int x, int y, Color color){        if(color == null){            throw new NullPointerException();            point = new Point(x, y);            this.color = color;        }    }    public Point asPoint(){        return point;    }    @Override    public boolean equals(Object o){        if(!(o instanceof ColorPoint)){            return false;        }        ColorPoint cp = (ColorPoint) o;        return cp.point.equals(point) && cp.color.equals(color);    }}

注意,你可以在一个抽象类的子类中增加新的值组件,而不会违反equals约定。

实现高质量equals方法的诀窍:
1. 使用==操作符检查参数是否为这个对象的引用。如果是则返回true,这是一种性能优化,如果比较操作的代价可能很高,就值得这么做。
2. 使用instanceof检查参数是否为正确的类型。
3. 把参数转换为正确的类型。
4. 对于该类的每个关键域,检查参数对象的域是否与之匹配。
域的比较要比简单的等同性测试复杂得多。
域的比较顺序影响equals方法的性能,为了获得最佳性能,应该最先比较最有可能不一致的域,或者开销最低的域。
5. 当你编写了equals方法之后,应该问自己三个问题:是否时对称的、传递的、一致的?
6. 覆盖equals总要覆盖hashCode
7. 不要企图让equals方法过于智能,过度地寻求各种等价关系很容易陷入麻烦之中。
8. 不要讲equals的参数类型Object换成其他类型。

第9条 覆盖equals时总有覆盖hashCode

hashCode的通用约定
1. 只要对用的equals方法的比较操作所用到的信息没有修改,那么对同一对象调用多次hashCode方法返回值必须一致。
2. 相等的对象必须有相等的hashCode
3. 如果两个对象equals返回false,hashCode值不一定要产生不同的结果。但是不同对象产生不同的hashCode有可能提高散列表的性能。

hashCode的计算过程中可以把冗余域(根据其他域可以计算出来的域)排除在外。
必须排除equals方法中没有用到的域
不要试图从hashCode计算中排除一个对象的关键部分来提高性能。

第10条 始终要覆盖toString

(略)

第11条 谨慎地覆盖clone

第12条 考虑实现Comparable接口

0 0
原创粉丝点击