如果类需要有“逻辑相等”概念(不同于对象等同的概念),且其父类中也没有实现同逻辑的equals()
方法时,譬如JDK8
中可以用自定义的类作为Map
的key
,这时我们可以将类覆盖Object.equals
方法。
通用约定
重写equals
方法,需要遵循以下几个约定:
1. 自反性
对于任何非null
的引用值x
,x.equals(x)
必须为true
2. 对称性
对于任何非null
的引用值x
和y
,当且仅当y.equals(x)
返回true
时,x.equals(y)
也必须返回为true
3. 传递性
对于任何非null
的引用值x
、y
、z
,若x.equals(y)
为true
,且y.equals(z)
为true
,则x.equals(z)
也必为true
4. 一致性
对于任何非null
的引用值x
和y
,只要equals
比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)
总会一直返回true
或者false
5. 对任何非null
的引用值x
,x.equals(null)
必须返回false
怎样写equals
方法
- 采用
==
来判断两个对象是否为同个对象,是返回true
,否则false
- 采用
instanceof
来判断参数是否具有正确的类型,否返回false
- 将参数强转为正确的类型
- 将类中每个有意义的属性与参数中对应的属性进行比较,相同则返回
true
,否则反之
1). 针对基本类型属性(除了float
和double
),直接采用==
判断
2). 针对引用类型,需要采用equals
进行判断。如果是自定义的类作为属性,该自定义的类也必须重写equals
方法
3). 针对float
属性,采用Float.compare
判断
4). 针对double
属性,采用Double.compare
判断
5). 针对数组属性,需要将每个元素按以上原则逐一比较。若每个元素均为很重要,可以采用Arrays.equals
方法
6). 针对某个属性可以为空的情形,需要指出防止抛出NullPointerException
异常,如(field == o.field || (field != null && field.equals(o.field)))
写完后,最好按照通用约定中的5条约定进行测试。
示例代码:
public final class PhoneNumber {
private final short areaCode;
private final short prefix;
private final short lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
rangeCheck(areaCode, 999, "area code");
rangeCheck(prefix, 999, "prefix");
rangeCheck(lineNumber, 9999, "line number");
this.areaCode = (short) areaCode;
this.prefix = (short) prefix;
this.lineNumber = (short) lineNumber;
}
private static void rangeCheck(int arg, int max, String name) {
if (arg < 0 || arg > max)
throw new IllegalArgumentException(name +": " + arg);
}
@Override public boolean equals(Object o) {
//==判断
if (o == this)
return true;
//instanceof判断
if (!(o instanceof PhoneNumber))
return false;
//各属性判断
PhoneNumber pn = (PhoneNumber)o;
return pn.lineNumber == lineNumber && pn.prefix == prefix && pn.areaCode == areaCode;
}
public int hashCode() {
//省略
}
}
注意点
- 重写
equals
方法,需要重写hashCode
方法 - 千万别写错了
equals
方法,原方法是public boolean equals(Object obj)
,参数是Object
类型,非指定的类型