Lombok EqualsAndHashCode註解
一、概述
在本教程中,我們將討論 Lombok 的@EqualsAndHashCode
註釋,它根據類的字段生成equals()
和hashCode()
方法。
2. @EqualsAndHashcode
的用法
Lombok 中的@EqualsAndHashCode
註解為給定類生成equals()
和hashCode()
方法,默認情況下使用所有非靜態和非瞬態字段。
要自定義使用的字段,我們可以使用@EqualsAndHashCode.Include
標記它們或使用@EqualsAndHashCode.Exclude
排除它們。
讓我們看一個例子來了解它是如何工作的:
@EqualsAndHashCode
public class Employee {
private String name;
private int id;
private int age;
}
在此示例中,我們定義了一個Employee
類,其中包含字段name
、 id,
和age,
並使用@EqualsAndHashCode
對其進行註釋。
讓我們看一下包含 Lombok 生成的equals()
、 hashCode()
和canEqual()
方法的編譯類:
public class EmployeeDelomboked {
private String name;
private int id;
private int age;
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Employee)) {
return false;
} else {
Employee other = (Employee) o;
if (!other.canEqual(this)) {
return false;
} else if (this.getId() != other.getId()) {
return false;
} else if (this.getAge() != other.getAge()) {
return false;
} else {
Object this$name = this.getName();
Object other$name = other.getName();
if (this$name == null) {
if (other$name == null) {
return true;
}
} else if (this$name.equals(other$name)) {
return true;
}
return false;
}
}
}
protected boolean canEqual(Object other) {
return other instanceof Employee;
}
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getId();
result = result * PRIME + this.getAge();
Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
return result;
}
}
在生成equals()
和hashCode()
方法時,Lombok 還會生成一個canEqual()
方法,用於檢查比較的對像是否與當前對像是同一類的實例。它確保equals()
方法僅在對象屬於同一類時才進行比較。
3. 從EqualsAndHashCode
中排除字段
Lombok 提供了幾個選項來從方法生成過程中排除特定字段。讓我們來看看這些選項。
3.1.使用@EqualsAndHashcode.Exclude
在Field
級別排除
我們可以使用@EqualsAndHashCode.Exclude
註釋來指示 Lombok從equals()
和hashCode()
方法的生成中排除一個字段:
@EqualsAndHashCode
public class Employee {
private String name;
@EqualsAndHashCode.Exclude
private int id;
@EqualsAndHashCode.Exclude
private int age;
}
在此示例中,我們使用@EqualsAndHashCode.Exclude
註釋從equals()
和hashCode()
方法生成中排除id
和age
字段。因此,Lombok 不會使用這些字段來生成方法:
public class EmployeeDelomboked {
//fields names
//standard getters and setters
//equals() implementation
@Override
public int hashCode() {
final int PRIME = 59;
int result = 1;
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
return result;
}
}
我們可以看到生成的*hashCode()*方法只是使用了name
字段。
3.2.在班級排除
我們還可以在類級別使用*@EqualsAndHashCode.Exclude*註解來排除字段:
@EqualsAndHashCode(exclude = {"id", "age"})
public class Employee {
private String name;
private int id;
private int age;
}
在此示例中,我們沒有直接使用@EqualsAndHashCode.Exclude
註釋字段。相反,我們在類級別應用註釋並指定要排除的字段名稱。無論如何,生成的equals()
和hashCode()
方法將是相同的。
4.包括特定字段
現在我們已經了解瞭如何排除字段,讓我們深入研究在equals()
和hashCode()
方法的生成中包括特定字段。
4.1.包含在字段級別
要在生成equals()
和hashCode()
方法時包含特定字段,我們可以使用@EqualsAndHashCode.Include
註釋。此註釋通常與**@EqualsAndHashCode
註釋上的onlyExplicitlyIncluded=true
**協同工作:
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class Employee {
@EqualsAndHashCode.Include
private String name;
@EqualsAndHashCode.Include
private int id;
private int age;
}
我們使用@EqualsAndHashCode.Include
設置onlyExplicitlyIncluded=true
屬性和帶註釋的name
和id
字段。 Lombok 僅使用這些字段生成equals()
和hashCode()
方法。它排除了age
字段,因為它沒有用@EqualsAndHashCode.Include
標記。
4.2.在方法級別包含
此外,我們可以將自定義方法的結果合併到equals()
和hashCode()
方法中,方法是使用@EqualsAndHashCode.Include:
@EqualsAndHashCode
public class Employee {
private String name;
private int id;
private int age;
@EqualsAndHashCode.Include
public boolean hasOddId() {
return id % 2 != 0;
}
}
在這裡,我們用@EqualsAndHashCode.Include
註解了hasOddId()
方法,表明 Lombok 在生成equals()
和hashCode()
方法時,除了字段之外,還應該使用該方法的結果:
public class EmployeeDelomboked {
//fields, equals method
//standard getters and setters
public int hashCode() {
final int PRIME = 59;
int result = 1;
result = result * PRIME + this.getId();
result = result * PRIME + this.getAge();
result = result * PRIME + (this.hasOddId() ? 79 : 97);
final Object $name = this.getName();
result = result * PRIME + ($name == null ? 43 : $name.hashCode());
return result;
}
}
我們可以看到hashCode()
方法包含hasOddId()
方法。
4.3.包括在班級
@EqualsAndHashCode
包含一個of
來定義包含的字段。在這種情況下,我們不在字段或方法中使用任何註釋:
@EqualsAndHashCode(of = {"name", "id"})
public class Employee {
private String name;
private int id;
private int age;
}
在這裡,我們使用of
屬性指定要包含在@EqualsAndHashCode
註釋中的字段name
和id
。通過這樣做,Lombok 將僅使用這些字段來生成equals()
和hashCode()
方法。
這種方法類似於使用@EqualsAndHashCode.Include
和onlyExplicitlyIncluded
屬性。 Lombok 實際上建議改用onlyExplicitlyIncluded
。 of
屬性預計將在未來被棄用。
EqualsAndHashCode
與Inheritance
當一個類擴展另一個類時,Lombok 的@EqualsAndHashCode
生成方法默認不會調用父類的equals()
和hashCode()
方法。但是,我們可以通過將callSuper
屬性設置為**true :**
,將父類的equals()
和hashCode()
包含在子類的equals()
和hashCode()
的生成中:
@EqualsAndHashCode(callSuper = true)
public class Manager extends Employee {
private String departmentName;
private int uid;
}
public class ManagerDelomboked extends Employee {
//fields
//equals implementation
public int hashCode() {
final int PRIME = 59;
int result = super.hashCode();
result = result * PRIME + this.getUid();
final Object $departmentName = this.getDepartmentName();
result = result * PRIME + ($departmentName == null ? 43 : $departmentName.hashCode());
return result;
}
}
在這裡,我們可以看到hashCode()
方法調用了super.hashCode()
方法。
6.龍目島@EqualsAndHashCode
配置
要配置@EqualsAndHashCode
註解的行為,我們需要在我們的項目中創建一個lombok.config
文件。該文件包含配置鍵,可用於修改生成的equals()
和hashCode()
方法的行為。
6.1. lombok.equalsAndHashCode.doNotUseGetters
lombok.equalsAndHashCode.doNotUseGetters = [true | false] (default: false)
配置鍵lombok.equalsAndHashCode.doNotUseGetters
可以設置為true
或false
(默認為false
)。
當設置為true
時,Lombok 在生成equals()
和hashCode()
方法時將直接訪問字段而不是使用 getter(如果可用)。但是,如果註釋參數doNotUseGetters
被顯式指定,則它優先於此設置。
6.2. lombok.equalsAndHashCode.callSuper
lombok.equalsAndHashCode.callSuper = [call | skip | warn] (default: warn)
配置鍵lombok.equalsAndHashCode.callSuper
可以設置為調用、 skip
或warn
(默認warn
)。
如果設置為調用,Lombok 將在為子類生成這些方法時包括對hashCode()
和equals()
的超類實現的調用。如果設置為skip
,則不會生成此類調用。默認行為類似於skip
,但有一個額外的警告。
6.3. lombok.equalsAndHashCode.flagUsage
lombok.equalsAndHashCode.flagUsage = [warning | error] (default: not set)
如果已配置,Lombok 會將@EqualsAndHashCode
的任何使用標記為警告或錯誤。
七、使用@EqualsAndHashCode
註解的優缺點
使用@EqualsAndHashCode
註解既有優點也有缺點。讓我們來看看其中的一些。
7.1.優點
Lombok 的@EqualsAndHashCode
註釋是一種有用的技術,可用於簡化equals()
和hashCode()
方法的創建。它提供自定義選項,例如排除字段或使用不同的哈希碼算法。
7.2.缺點
使用 Lombok 的@EqualsAndHashCode
註釋有幾個缺點。首先,當兩個對象具有不同的字段值但哈希碼相同時,它會產生不需要的哈希碼衝突。
其次,它可能與依賴equals()
和hashCode()
傳統實現的其他庫和框架不兼容。
最後,它會生成未在類中明確定義的方法,從而降低可讀性,使理解代碼邏輯變得更加困難。
8. 常見問題
在具有雙向關係(例如父子關係)的類上使用 Lombok 的@EqualsAndHashCode
註解時,可能會出現java.lang.StackOverflowError
。
讓我們看一個@EqualsAndHashCode
如何導致問題的例子:
@EqualsAndHashCode
public class Employee {
private String name;
private int id;
private int age;
private Manager manager;
}
@EqualsAndHashCode
public class Manager {
private String name;
private Employee assistantManager;
}
在此示例中, Employee
和Manager
類具有雙向關係並使用@EqualsAndHashCode
。當 Employee 的實例調用其hashCode()
方法時,它將觸發對Manager
類的hashCode()
方法的調用,該方法將遞歸回調到Employee
的hashCode()
方法。這個遞歸循環將一直持續到程序用完堆棧空間並拋出錯誤為止。
為避免此問題,我們可以使用@EqualsAndHashCode
的 exclude 或onlyExplicitlyIncluded
屬性從hashCode()
和equals()
方法的生成中排除其中一個類。例如,我們可以從Employee
類的hashCode()
計算中排除manager
字段:
@EqualsAndHashCode(exclude = "manager")
public class EmployeeV2 {
private String name;
private int id;
private int age;
private Manager manager;
}
9.結論
在本文中,我們了解了 Lombok @EqualsAndHashCode
註釋如何生成equals()
和hashCode()
方法。我們還探討了字段和類級別的包含和排除如何自定義生成的方法。
使用 Lombok @EqualsAndHashCode
註解可以省時省力。但是,我們應該注意潛在的問題,例如哈希碼衝突、與其他庫的不兼容以及雙向關係中的StackOverflowError
。
與往常一樣,可以在 GitHub 上找到本文的代碼。