JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

java 核心技术-12版 卷Ⅰ- 5.2.3相等测试与继承

wys521 2024-11-28 09:01:34 精选教程 30 ℃ 0 评论

原文

5.2.3相等测试与继承

如果隐式和显式的参数不属于同一个类,equals 方法将如何处理呢?这是一个很有争议的问题。在前面的例子中,如果发现类不能完全匹配,equals 方法就返回 false。但是,许多程序员喜欢使用 instanceof 进行检测:

if (!(otherObject instanceof Employee))

return false;

这样就允许 otherObject 属于一个子类。但是这种方法可能会招致一些麻烦。下面会解释原因。Java语言规范要求 equas 方法具有下述性质:

1. 自反性:对于任何非 null 引用 x,x.equals(x)应该返回 true。

2. 对称性:对于任何引用x和y,当且仅当y.equals(x) 返回 true 时,x.equals(y) 返回 true

3.传递性:对于任何引用x、y和z,如果 x.equals(y) 返回 true,y.equals(z) 返回 true,则x.equals(z)也应该返回 true。

4.一致性: 如果x和y引用的对象没有发生变化,则反复调用 x.equals(y) 应该返回同样的结果。

5.对于任意非 null引用x,x.equals(null) 应该返回 false。

这些规则当然很合理。你肯定不希望类库实现者在查找数据结构中的一个元素时纠结调

用 x.equals(y) 还是调用 y.equals(x)。

不过,就对称性规则来说,当参数属于不同的类时会有一些微妙的结果。请看下面这个

调用:

e.equals(m)

这里的e是一个Employee对象,m是一个Manager 对象,并且这两个对象恰好有相同的姓名薪水和雇用日期。如果在 Employee.equals 使用 instanceof 进行检测,则这个调用将返回 true不过这意味着,如果反过来调用:

m.equals(e)也需要返回 true。对称性规则不允许这个方法调用返回 false 或者抛出异常。

这就使得 Manager类陷人困境。这个类的 equals 方法必须愿意将自己与任何一个 Employee对象进行比较,而不考虑经理特有的那部分信息!猛然间这让人感觉 instanceof 测试并不是那么好。

有些作者认为 getClass 检测是有问题的,因为它违反了替换原则。有一个经常提到的例子,就是AbstractSet 类的 equals 方法,它将检测两个集合是否有相同的元素。AbstractSet类有两个具体子类:TreeSet 和 HashSet。它们分别使用不同的算法查找集合元素。你肯定希望能够比较任意的两个集合,而不论它们如何实现。

不过,这个集合例子非常特殊,最好将 AbstractSet.equals 声明为 final,因为不应该重新定义集合的相等语义(但事实上,这个方法并没有声明为 final。这是为了让子类实现更高效的算法来完成相等性检测)。

就现在来看,有两种完全不同的情形:

  • 如果子类可能有自己的相等性概念,则对称性需求强制使用 getclass 检测。
  • 如果由超类决定相等性概念,那么可以使用 instanceof 检测,这样不同子类的对象也可能相等。

在员工和经理的例子中,只要对应的字段相等,就认为两个对象相等。如果两个 Managel对象的姓名、薪水和雇用日期均相等,而奖金不相等,就认为它们不同,因此,我们要使用getClass 检测。

但是,假设使用员工的ID 来检测相等性,并且这个相等性概念适用于所有的子类,就可以使用 instanceof检测,而且应该将 Employee.equals 声明为 final。

注释 : 在标准Java库中包含 150多个equals 方法的实现,包括使用 instanceof 检测调用getClass、捕获 ClassCastException 或者什么也不做等各种不同做法。可以查看 java.sql.Timestamp类的API文档,在这里实现人员不无尴尬地指出,他们让自己陷入了困境。Timestamp类继承自java.util.Date,而后者的 equals 方法使用了一个instanceof检测,这样一来就无法覆盖 equals,使之同时做到对称且正确。


下面给出编写完美equals 方法的技巧:

  1. 将显式参数命名为 otherObject,稍后需要将它强制转换成另一个名为 other 的变量
  2. 检测 this 与 otherobject 是否相同:
  3. if (this = otherObject) return true;

这条语句只是一个优化。实际上,这种情况很常见,因为检查同一性要比逐个比较字段开销小。

3.检测otherObject 是否为 null,如果为 null,则返回 false。这个检测是必要的

if (otherObject == null) return false;

4.比较 this 与 otherObject 的类。如果 equals 的语义可以在子类中改变,就使用 getClass 检测:

if (getClass() != other0bject.getClass())

?return false;

ClassName other = (ClassName) otherObject;

如果所有的子类都有相同的相等性语义,则可以使用 instanceof 检测:

if (!(otherObject instanceof ClassName other)) return false;

注意,如果 instanceof 检测成功,它会把 other 设置为 otherobject。不再需要强制类型转换。

5. 现在根据相等性概念的要求来比较字段。使用== 比较基本类型字段,使用 Objects.equals 比较对象字段。如果所有的字段都匹配,就返回 true; 否则,返回 false。

?return field1 = other.field1

? ?&&Objects.equals(field2,other.field2)

? ?&&...


如果在子类中重新定义equals,就要在其中包含一个 super.equals(other) 调用。

提示:对于数组类型的字段,可以使用静态的 Arrays.equals 方法检查相应的数组元素是否相等。对于多维数组,可以使用Arrays.deepEquals 方法。


警告:下面是实现 equals 方法时常见的一个错误。你能找到其中的问题吗?

public class Employee{
	public boolean equals(Employee other){
    	return other != null 
          && getClass() == other.getClass()
          && Objects.equals(name, other.name)
          && salany == other.salary
          && Objects.equals(hireDay,other.hireDay);
    }


}


这个方法声明的显式参数类型是 Employee。因此,它没有覆盖 Object 类的 equals 方

法,而是定义了一个完全无关的方法。

为了避免发生这种错误,可以使用 @Override 标记要覆盖超类方法的那些子类方法:

@Override public boolean equals(0bject other)

如果犯了错误,没有覆盖方法而是在定义一个新方法,编译器就会报告一个错误,例如,假设将下面的声明添加到 Employee 类中:

@Override public boolean equals(Employee other)

就会报告一个错误,因为这个方法并不会覆盖超类 bject 中的任何方法



API : java.util.Arrays JDK1.2

static boolean equals(xx[] a, xx[] b) JDK5

如果两个数组长度相同,并且对应位置上的元素也相同,则返回 true。数组的元素类型x 可以是 Object、int、long、short、char、 byte、 boolean、 float 或 double。


API : java.util.0bjects JDK 7

static boolean equals(Object a, Object b)

如果a和b都为null,则返回 true; 如果只有其中之一为 null,则返回 false; 否

返回a.equals(b)。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表