一般不要覆盖equals方法,若一定要覆盖equals,要遵守通用约定
package codeTemplate.effectiveJava.bean;
public class PhoneNumber {
private short areaCode, prefix, lineNum;
@Override
public boolean equals(Object obj) {
//使用==操作符检查“参数是否为这个对象的引用”
if (obj == this) {
return true;
}
//使用instanceof操作符检查“参数是否为正确的类型”
if (!(obj instanceof PhoneNumber)) {
return false;
}
//把参数转化为正确的类型
PhoneNumber phoneNumber = (PhoneNumber) obj;
//对于该类中的每一个关键域,检查参数中的域是否与该对象中对应的域相匹配
return phoneNumber.areaCode == this.areaCode && phoneNumber.prefix == this.prefix && phoneNumber.lineNum == this.lineNum;
//编写完equals方法后,问自己:他是否是对称的,传递的,一致的
}
}
覆盖equals时一定要覆盖hashCode
@Override
public int hashCode() {
//Object类有一个静态方法,它带有任意数量的对象,并为他们返回一个散列码
return Objects.hash(areaCode, prefix, lineNum);
}
让IDE生成equals和hashCode方法,通常优于手工实现它们
IDE自动生成的方法
package codeTemplate.effectiveJava.bean;
import java.util.Objects;
public class PhoneNumber {
private short areaCode, prefix, lineNum;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PhoneNumber that = (PhoneNumber) o;
if (areaCode != that.areaCode) return false;
if (prefix != that.prefix) return false;
return lineNum == that.lineNum;
}
@Override
public int hashCode() {
int result = areaCode;
result = 31 * result + (int) prefix;
result = 31 * result + (int) lineNum;
return result;
}
}
始终要覆盖toStri0ng
使用IDE自动生成,可自己做调整
@Override
public String toString() {
return "PhoneNumber{" +
"areaCode=" + areaCode +
", prefix=" + prefix +
", lineNum=" + lineNum +
'}';
}
谨慎地覆盖clone
clone方法就是另一个构造器,必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
默认方法,只会克隆基本类型的值,应用类型的引用,自己实现时需要修正任何需要修正的域
package codeTemplate.effectiveJava.bean;
import java.util.Objects;
public class PhoneNumber {
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
@Override
protected Object clone() throws CloneNotSupportedException {
PhoneNumber clone = (PhoneNumber) super.clone();
clone.phoneArea = this.phoneArea;
return phoneArea;
}
}
对象拷贝的更好的方法是提供一个拷贝构造器或拷贝工厂
拷贝构造器
浅拷贝
public class PhoneNumber {
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
PhoneNumber(PhoneNumber phoneNumber) {
this.areaCode= phoneNumber.areaCode;
this.prefix= phoneNumber.prefix;
this.lineNum= phoneNumber.lineNum;
this.phoneArea= phoneNumber.phoneArea;
}
}
深拷贝
PhoneNumber(PhoneNumber phoneNumber) {
this.areaCode= phoneNumber.areaCode;
this.prefix= phoneNumber.prefix;
this.lineNum= phoneNumber.lineNum;
this.phoneArea = new PhoneArea(phoneNumber.phoneArea.getCountry());
}
package codeTemplate.effectiveJava.bean;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class PhoneArea {
private String Country;
public PhoneArea(String country) {
Country = country;
}
}
拷贝构造方法相比于clone方法的优势:
- 拷贝构造方法实现更简单。不需要实现 Cloneable 接口,也不需要处理 CloneNotSupportedException
- clone 函数返回一个普通的 Object 类型的引用。还需要转成特定的类型。
- 在 clone 方法中不能为 final 属性赋值,但是在拷贝构造方法中就可以。
拷贝工厂
方法一:
package codeTemplate.effectiveJava.bean;
import java.util.Objects;
public class PhoneNumber {
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
public PhoneNumber(PhoneNumber phoneNumber) {
this.areaCode= phoneNumber.areaCode;
this.prefix= phoneNumber.prefix;
this.lineNum= phoneNumber.lineNum;
this.phoneArea = new PhoneArea(phoneNumber.phoneArea.getCountry());
}
public static PhoneNumber newInstance(PhoneNumber phoneNumber) {
return new PhoneNumber(phoneNumber);
}
}
方法二:
package codeTemplate.effectiveJava.bean;
import java.util.Objects;
public class PhoneNumber {
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
public PhoneNumber() {
}
public static PhoneNumber newInstance(PhoneNumber phoneNumber) {
PhoneNumber pn = new PhoneNumber();
pn.areaCode= phoneNumber.areaCode;
pn.prefix= phoneNumber.prefix;
pn.lineNum= phoneNumber.lineNum;
pn.phoneArea = new PhoneArea(phoneNumber.phoneArea.getCountry());
return pn;
}
}
考虑实现Comparable接口
每当实现一个对排序敏感的类时,都应该让这个类实现Comparable接口,以便其实例可以轻松地被分类、搜索、以及用在基于比较的集合中。每当在compareTo方法的实现中比较域值时,都要避免使用<和>操作符,而应该在装箱基本类型的类中使用静态的compare方法,或者在Comparator接口中使用比较器构造方法。
package codeTemplate.effectiveJava.bean;
public class PhoneNumber implements Comparable<PhoneNumber>{
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
@Override
public int compareTo(PhoneNumber phoneNumber) {
int result = Short.compare(areaCode, phoneNumber.areaCode);
if (result == 0) {
result = Short.compare(prefix, phoneNumber.prefix);
if (result == 0) {
result = Short.compare(lineNum, phoneNumber.lineNum);
}
}
return result;
}
}
若没有实现Comparable接口,可以使用一个显式的Comparator来代替
package codeTemplate.effectiveJava.bean;
import lombok.Getter;
import lombok.Setter;
import java.util.Comparator;
@Getter
@Setter
public class PhoneNumber {
private short areaCode, prefix, lineNum;
private PhoneArea phoneArea;
private static final Comparator<PhoneNumber> COMPARATOR = Comparator.comparingInt((PhoneNumber pn) -> pn.areaCode)
.thenComparingInt(pn -> pn.prefix).thenComparingInt(pn -> pn.lineNum);
public int compareTo(PhoneNumber phoneNumber) {
return COMPARATOR.compare(this, phoneNumber);
}
}