第3章 对于所有对象都通用的方法

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

不覆盖equals方法的情况下,类的每个实例都只与它自身相等。

如果满足了以下任何一个条件,这就正是所期望的结果:

  • 类的每个实例本质上都是唯一的。Thread
  • 不关心类是否提供了逻辑相等的测试功能。RandomObject类继承了equals
  • 超类已经覆盖了equals,从超类继承过来的行为对于子类也是合适的。SetAbstractSet继承equals实现,ListAbstractList继承equals实现,MapAbstractMap继承equals实现
  • 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。在这种情况下,无疑是应该覆盖equals方法的,以防止它被意外调用:
@Override public boolean equals(Object o) {
    throw new AssertionError(); // method is never called
}

如果类有自己特有的逻辑相等概念,而且超类没有覆盖equals以实现期望的行为,这时我们就需要覆盖equals方法。

equals方法实现了等价关系:自反性,对称性,传递性,一致性,非空性

实现高质量equals方法的诀窍:

  • 使用==操作符检查参数是否为这个对象的引用
  • 使用instanceof操作符检查参数是否为正确的类型
  • 把参数转换为正确的类型
  • 对于该类中的每个关键域,检查参数中的域是否与该对象中对应的域相匹配

9.覆盖equals时总要覆盖hashCode

在每个覆盖了equals方法的类中,也必须覆盖hashCode方法,如果不这样做的话,就会违反Object.hashCode的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这样的集合包括HashMapHashSetHashtable

相等的对象必须具有相等的散列码。

@Override public int hashCode() { return 42; } // never use

每个对象都具有相同的散列码,每个对象都被映射到同一个散列桶中,使散列表退化为链表,使得本该线性时间运行的程序变成了以平方级时间在运行。

10.始终要覆盖toString

在实际应用中,toString方法应该返回对象中包含的所有值得关注的信息。

11.谨慎地覆盖clone

Java1.5版本引入了协变返回类型(covariant return type)作为泛型,换句话说,目前覆盖方法的返回类型可以是被覆盖方法的返回类型的子类了。

clone方法就是另一个构造器,你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。clone架构与引用可变对象的final域的正常用法是不相兼容的。

    /**
     * Creates a shallow copy of this hashtable. All the structure of the
     * hashtable itself is copied, but the keys and values are not cloned.
     * This is a relatively expensive operation.
     *
     * @return  a clone of the hashtable
     */
    public synchronized Object clone() {
        try {
            Hashtable<?,?> t = (Hashtable<?,?>)super.clone();
            t.table = new HashtableEntry<?,?>[table.length];
            for (int i = table.length ; i-- > 0 ; ) {
                t.table[i] = (table[i] != null)
                    ? (HashtableEntry<?,?>) table[i].clone() : null;
            }
            t.keySet = null;
            t.entrySet = null;
            t.values = null;
            t.modCount = 0;
            return t;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

另一个实现对象拷贝的好办法是提供一个拷贝构造器(copy constructor)或拷贝工厂(copy factory)。拷贝构造器只是一个构造器,它唯一的参数类型是包含该构造器的类。

基于接口的拷贝构造器和拷贝工厂(conversion constructor & conversion factory),允许用户选择拷贝的实现类型,而不是强迫客户接受原始的实现类型。

12.考虑实现Comparable接口

一旦类实现了Comparable接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。付出很小的努力就可以获得非常强大的功能。事实上,Java平台类库中的所有值类都实现了Comparable接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代排序,那你就应该坚决考虑实现这个接口:

public interface Comparable<T> {
    int compareTo(T t);
}

compareTo方法的通用约定与equals方法相似。

比较整数型基本类型的域,可以使用关系操作符<>。浮点域用Double.compare或者Float.compare,而不用关系操作符,浮点值没有遵守compareTo的通用约定。对于数组域,则要把这些指导原则应用到每个元素上。

results matching ""

    No results matching ""