重寫Java中equals和hashcode方法的一般規則

參考:
Java核心技術
https://blog.csdn.net/nwpu_geeker/article/details/78760157

Object簡介

Object 類是 Java 中所有類的始祖, 在 Java 中每個類都是由它擴展而來的。如果一個類沒有明確地指出其超類,Object就被認爲是這個類的超類(父類)。Object類聲明瞭以下函數:

1) protected Object clone()        
2) Class<? extends Object> getClass()  
3) String toString()             
4) int hashCode()        
5) boolean equals(Object obj) 
6) void wait()  
7) void wait(long timeout)     
8) void wait(long timeout, int nanos) 
9) void notify()        
10) void notifyAll()  
11)protected void finalize()

其中比較重要且常用的有equals、hashcode、toString、getClass等方法。

1 equals方法

在 Object 類中,equals方法將判斷兩個對象是否具有相同的引用。這與Java中“==”的功能是一樣的。換句話說,Object類中的equals方法並不會比較對象的內容(狀態)。正因爲如此,其他類往往需要重寫equals方法比較對象的內容。例如, 如果兩個僱員對象的ID(key)一樣, 就認爲它們是相等的,此時equals方法比較僱員對象的ID纔可以確定2個對象是否相等。

1.1 如何重寫equals方法

1)顯式參數命名爲 otherObject,稍後需要將它轉換成另一個叫做other的變量(強制類型轉換時用到)。
2)檢測 this(隱式參數)與otherObject是否引用同一個對象:

if (this ==otherObject) return true;

實際上,這是一種經常採用的形式。因爲計算這個等式要比一個一個地比較類中所有屬性要快得多。
3)檢測otherObject是否爲null, 如果爲null,返回false。這項檢測是很必要的。

if (otherObject == null) return false;

4)比較this與otherObject是否屬於同一個類,此時有2種情況。
a)如果equals的語義在每個子類中有所改變,就使用getClass檢測:

if (getClass()  != otherObject.getClass() ) return false;

b)如果所有的子類都擁有統一的語義,即所有子類包括父類都使用相同的屬性比較是否相等,就使用instanceof檢測:

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

5)將 otherObject 強制類型轉換爲相應的類類型變量:

ClassName other = (ClassName) otherObject

6)開始對所有需要比較的域進行比較。使用“==”比較基本類型域,使用equals比較對象域。如果所有的域都匹配,就返回true;否則返回false。

return field1 == other.field
&& Objects.equals(field2, other.field2)
&& ...

如果在子類中重新定義equals,就需要在子類中的equals方法中首先調用super.equals(other),然後再加上子類特有域比較是否相等。

1.2 工具類Objects的equals方法

 static boolean equals(Object a, Object b)

如果a和b都爲null,返回 true ; 如果只有其中之一爲 null, 則返回 false ; 否則返 回a.equals(b)。

1.3 對(4)的解釋

Java核心技術一書中說,如果子類能夠擁有自己的相等概念,那麼對稱性需求將強制採用getClass進行檢測。
這句話的意思是:只要子類所有對應的域相等就認爲2個對象相等,則表示子類擁有自己的相等概念。
如果由超類決定相等的概念,那麼就使用instanceof進行檢測。
也就是說,2個對象是否相等由超類的屬性決定,而和子類的屬性完全無關時,就使用instanceof。

2 hashcode方法

equals方法與hashcode方法必須保持一致,即2個相等的對象的散列碼也應該相等。對象的散列碼錶示對象的存儲地址。

2.1 Objects的hashcode方法

static int hash(Object . .. objects )

返回一個散列碼,由提供的所有對象的散列碼組合而得到。

static int hashCode(Object a )

如果a爲null返回0,否則返回 a.hashCode() 。使用該方法比使用a.hashcode()要好,主要是當a是null時,該方法會拋出異常。

3 示例

假設Manager類繼承自Employee類,而Employee類有name,salary和hireDay等屬性,而子類Manager新增屬性bonus。
Employee類的代碼如下:

import java.time.*;
import java.util.Objects;

public class Employee 
{
	private String name;
	private double salary;
	private LocalDate hireDate;

	public Employee(){}
	public Employee(String name,double salary,int year,int month,int day)
	{
		this.name = name;
		this.salary = salary;
		hireDate = LocalDate.of(year, month, day);
	}
	// getter setter等省略
	public void raiseSalary(double byPercent){
		double raise = salary *  byPercent/100;
		salary += raise;
		
	}
	
	@Override
	public boolean equals(Object otherObject) {
		if(this == otherObject) return true;
		if(otherObject == null) return false;
		
		if(getClass() != otherObject.getClass()) return false;
		
		Employee other = (Employee) otherObject;
		
		return Objects.equals(name, other.name)
				&& salary == other.salary
				&& Objects.equals(hireDate, other.hireDate);
	}
	@Override
	public int hashCode() {
		
		return Objects.hash(name,salary,hireDate);
	}
}

Manager類的定義如下:

public class Manager extends Employee {

	private double bonus;
	public Manager(String name,double salary,int year,int month,int day){
		super(name,salary,year,month,day);
		this.bonus = 0;
	}
	
	public double getSalary(){
		double baseSalary = super.getSalary();
		return baseSalary + bonus;
	}
	
	public void setBonus(double b){
		bonus = b;
	}
	
	@Override
	public boolean equals(Object otherObject) {
	// 子類首先調用父類的equals方法
		if( !super.equals(otherObject)) return false;
		Manager other = (Manager)otherObject;
		return bonus == other.bonus;
	}
	
	@Override
	public int hashCode() {
	// 子類首先調用父類的hashcode,然後加上自己屬性的hashcode
		return super.hashCode()+17*new Double(bonus).hashCode();
	}
	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章