Lombok中關(guān)于@Data的使用解析
當(dāng)你在使用 Lombok 的 @Data 注解時(shí),其實(shí)會(huì)有一些坑需要關(guān)注,今天就讓我們來(lái)見(jiàn)識(shí)一下。
Lombok
先來(lái)簡(jiǎn)單介紹一下 Lombok ,其官方介紹如下:
Project Lombok makes java a spicier language by adding 'handlers' that know how to build and compile simple, boilerplate-free, not-quite-java code.
大致意思是 Lombok 通過(guò)增加一些"處理程序",可以讓 Java 代碼變得簡(jiǎn)潔、快速。
Lombok 提供了一系列的注解幫助我們簡(jiǎn)化代碼,比如:
注解名稱 | 功能 |
---|---|
@Setter | 自動(dòng)添加類中所有屬性相關(guān)的 set 方法 |
@Getter | 自動(dòng)添加類中所有屬性相關(guān)的 get 方法 |
@Builder | 使得該類可以通過(guò) builder (建造者模式)構(gòu)建對(duì)象 |
@RequiredArgsConstructor | 生成一個(gè)該類的構(gòu)造方法,禁止無(wú)參構(gòu)造 |
@ToString | 重寫(xiě)該類的toString()方法 |
@EqualsAndHashCode | 重寫(xiě)該類的equals()和hashCode()方法 |
@Data | 等價(jià)于上面的@Setter、@Getter、@RequiredArgsConstructor、@ToString、@EqualsAndHashCode |
看起來(lái)似乎這些注解都很正常,并且對(duì)我們的代碼也有一定的優(yōu)化,那為什么說(shuō)@Data注解存在坑呢?
@Data注解
內(nèi)部實(shí)現(xiàn)
由上面的表格我們可以知道,@Data是包含了@EqualsAndHashCode的功能,那么它究竟是如何重寫(xiě)equals()和hashCode()方法的呢?
我們定義一個(gè)類TestA:
@Data public class TestA { String oldName; }
我們將其編譯后的 class 文件進(jìn)行反編譯:
public class TestA { String oldName; public TestA() { } public String getOldName() { return this.oldName; } public void setOldName(String oldName) { this.oldName = oldName; } public boolean equals(Object o) { // 判斷是否是同一個(gè)對(duì)象 if (o == this) { return true; } // 判斷是否是同一個(gè)類 else if (!(o instanceof TestA)) { return false; } else { TestA other = (TestA) o; if (!other.canEqual(this)) { return false; } else { // 比較類中的屬性(注意這里,只比較了當(dāng)前類中的屬性) Object this$oldName = this.getOldName(); Object other$oldName = other.getOldName(); if (this$oldName == null) { if (other$oldName != null) { return false; } } else if (!this$oldName.equals(other$oldName)) { return false; } return true; } } } protected boolean canEqual(Object other) { return other instanceof TestA; } public int hashCode() { int PRIME = true; int result = 1; Object $oldName = this.getOldName(); int result = result * 59 ($oldName == null ? 43 : $oldName.hashCode()); return result; } public String toString() { return "TestA(oldName=" this.getOldName() ")"; } }
針對(duì)其equals()方法,當(dāng)它進(jìn)行屬性比較時(shí),其實(shí)只比較了當(dāng)前類中的屬性。如果你不信的話,我們?cè)賮?lái)創(chuàng)建一個(gè)類TestB,它是TestA的子類:
@Data public class TestB extends TestA { private String name; private int age; }
我們將其編譯后的 class 文件進(jìn)行反編譯:
public class TestB extends TestA { private String name; private int age; public TestB() { } public String getName() { return this.name; } public int getAge() { return this.age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof TestB)) { return false; } else { TestB other = (TestB)o; if (!other.canEqual(this)) { return false; } else { // 注意這里,真的是只比較了當(dāng)前類中的屬性,并沒(méi)有比較父類中的屬性 Object this$name = this.getName(); Object other$name = other.getName(); if (this$name == null) { if (other$name == null) { return this.getAge() == other.getAge(); } } else if (this$name.equals(other$name)) { return this.getAge() == other.getAge(); } return false; } } } protected boolean canEqual(Object other) { return other instanceof TestB; } public int hashCode() { int PRIME = true; int result = 1; Object $name = this.getName(); int result = result * 59 ($name == null ? 43 : $name.hashCode()); result = result * 59 this.getAge(); return result; } public String toString() { return "TestB(name=" this.getName() ", age=" this.getAge() ")"; } }
按照代碼的理解,如果兩個(gè)子類對(duì)象,其子類中的屬性相同、父類中的屬性不同時(shí),利用equals()方法時(shí),依舊會(huì)認(rèn)為這兩個(gè)對(duì)象相同,測(cè)試一下:
public static void main(String[] args) { TestB t1 = new TestB(); TestB t2 = new TestB(); t1.setOldName("123"); t2.setOldName("12345"); String name = "1"; t1.name = name; t2.name = name; int age = 1; t1.age = age; t2.age = age; System.out.println(t1.equals(t2)); System.out.println(t2.equals(t1)); System.out.println(t1.hashCode()); System.out.println(t2.hashCode()); System.out.println(t1 == t2); System.out.println(Objects.equals(t1, t2)); }
結(jié)果為:
true
true
6373
6373
false
true
問(wèn)題總結(jié)
對(duì)于父類是Object且使用了@EqualsAndHashCode(callSuper = true)注解的類,這個(gè)類由 Lombok 生成的equals()方法只有在兩個(gè)對(duì)象是同一個(gè)對(duì)象時(shí),才會(huì)返回 true ,否則總為 false ,無(wú)論它們的屬性是否相同。
這個(gè)行為在大部分時(shí)間是不符合預(yù)期的,equals()失去了其意義。即使我們期望equals()是這樣工作的,那么其余的屬性比較代碼便是累贅,會(huì)大幅度降低代碼的分支覆蓋率。
解決方法
用了@Data就不要有繼承關(guān)系,類似 Kotlin 的做法。
自己重寫(xiě)equals(), Lombok 不會(huì)對(duì)顯式重寫(xiě)的方法進(jìn)行生成。
顯式使用@EqualsAndHashCode(callSuper = true), Lombok 會(huì)以顯式指定的為準(zhǔn)。
Lombok的@Data踩坑記錄
面試問(wèn)你@Data注解的作用,一般人回答就是生成get/set/toString
真是這樣嗎?
其實(shí)不然,其實(shí)@Data注解作用是
get/set
無(wú)參數(shù)構(gòu)造器
toString
hashcode
equals
@Data會(huì)自動(dòng)生成hashcode和equals方法,一般人會(huì)把這點(diǎn)忘了
證明
idea使用alt+6查看類的具體屬性和方法
小結(jié)一下
***@Data會(huì)自動(dòng)生成以下方法***
get/set
無(wú)參數(shù)構(gòu)造器
toString
hashcode
equals
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- lombok注解@Data使用在繼承類上時(shí)出現(xiàn)警告的問(wèn)題及解決
- lombok中@Data使用常見(jiàn)的小坑及解決
- 關(guān)于Lombok @Data注解:簡(jiǎn)化Java代碼的魔法棒
- 使用lombok@Data啟動(dòng)項(xiàng)目報(bào)錯(cuò)問(wèn)題及解決
- 使用lombok的@Data會(huì)導(dǎo)致棧溢出StackOverflowError問(wèn)題
- Lombok同時(shí)使?@Data和@Builder踩坑總結(jié)
- 使用lombok@Data存在extends時(shí)需要注意的問(wèn)題
相關(guān)文章
Jmeter?BlazeMeter實(shí)現(xiàn)web錄制過(guò)程
BlazeMeter是一款與Apache JMeter兼容的chrome插件,采用BlazeMeter可以方便的進(jìn)行流量錄制和腳本生成,作為接口測(cè)試腳本編寫(xiě)的一個(gè)基礎(chǔ),這篇文章主要介紹了Jmeter?BlazeMeter實(shí)現(xiàn)web錄制,需要的朋友可以參考下2021-12-12springboot利用@Aspect實(shí)現(xiàn)日志工具類的詳細(xì)代碼
這篇文章主要介紹了springboot利用@Aspect實(shí)現(xiàn)日志工具類,通過(guò)實(shí)例代碼介紹了導(dǎo)包及在啟動(dòng)類上進(jìn)行注解自動(dòng)掃描的方法,需要的朋友可以參考下2022-03-03通過(guò)一個(gè)map替換字符串中指定的字符變量方法
下面小編就為大家?guī)?lái)一篇通過(guò)一個(gè)map替換字符串中指定的字符變量方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03SpringMVC多個(gè)文件上傳及上傳后立即顯示圖片功能
這篇文章主要介紹了SpringMVC多個(gè)文件上傳及上傳后立即顯示圖片功能,非常不錯(cuò),具有參考借鑒價(jià)值功能,需要的朋友可以參考下2017-10-10SpringBoot源碼分析之bootstrap.properties文件加載的原理
本文通過(guò)訪問(wèn)看到bootstrap.properties中的信息獲取到了,同時(shí)age也被application.properties中的屬性覆蓋掉了。加載順序到底是什么?為什么會(huì)覆蓋呢?我們接下來(lái)分析下吧2021-12-12詳解idea中web.xml默認(rèn)版本問(wèn)題解決
這篇文章主要介紹了詳解idea中web.xml默認(rèn)版本問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12IDEA2020.1個(gè)性化設(shè)置的實(shí)現(xiàn)
這篇文章主要介紹了IDEA2020.1個(gè)性化設(shè)置的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08