JDK 14的新特性:Lombok的終結者record
簡介
自從面向對象產生之后,程序界就開始了新的變化,先是C發展到了C++,后面java橫空出世,大有一統江湖的趨勢。
面向對象憑借其結構化的特點和在大型項目中的優勢,一路蓬勃發展到今。面向對象不是不好,但是太繁瑣。
比如我們要定義一個簡單的存儲數據的結構,比如說User。除了要定義其內部的具體字段以外,我們還要定義get set方法,定義構造函數,equals(), hashCode(), toString()等。
為了解決這個問題,也產生了很多解決方案,比如Lombok,可以通過注解就自動生成特定的存取方法和構造函數。但是Lombok生成的代碼看不到,在代碼調試方面有一定的劣勢。
終於JDK 14為我們帶來了record,雖然還是預覽特性,但是今天我們一覽為快。
新的Record類型
Record是一種輕量級的class,可以看做是數據結構體。和scala中的case有點相似。
舉個自定義User的例子看一下Record是怎么用的:
public record Address(
String addressName,
String city
) {
}
public record CustUser(
String firstName,
String lastName,
Address address,
int age
) {}
上面我們定義了兩個類,CustUser和Address。CustUser中引用了Address。
Record和普通的類的區別就在於Record多了一個括號括起來的定義的字段。
Record類默認是final的,里面的字段默認是private final的。
探討Record的秘密
要想知道Record到底是怎么工作的,我們可以使用javap來對編譯好的class文件反編譯,運行javap CustUser,可以得到下面的結果:
警告: 二進制文件CustUser包含com.flydean.records.CustUser
Compiled from "CustUser.java"
public final class com.flydean.records.CustUser extends java.lang.Record {
public com.flydean.records.CustUser(java.lang.String, java.lang.String, com.flydean.records.Address, int);
public java.lang.String toString();
public final int hashCode();
public final boolean equals(java.lang.Object);
public java.lang.String firstName();
public java.lang.String lastName();
public com.flydean.records.Address address();
public int age();
}
上面可以看到final class CustUser繼承自java.lang.Record。
並且自動添加了默認帶有所有字段的構造函數。各個自動的獲取方法,並實現了toString,hashCode和equals方法。
天啦,太完美了,我們想要的它居然都有。
如果上面的javap還不是很清楚的話,大家可以借助IDE的反編譯功能,打開CustUser.class文件看一看:
public final class CustUser extends java.lang.Record {
private final java.lang.String firstName;
private final java.lang.String lastName;
private final com.flydean.records.Address address;
private final int age;
public CustUser(java.lang.String firstName, java.lang.String lastName, com.flydean.records.Address address, int age) { /* compiled code */ }
public java.lang.String toString() { /* compiled code */ }
public final int hashCode() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public java.lang.String firstName() { /* compiled code */ }
public java.lang.String lastName() { /* compiled code */ }
public com.flydean.records.Address address() { /* compiled code */ }
public int age() { /* compiled code */ }
}
注意,上面的反編譯我們可以看到,record中的所有字段都是final的,只能在初始化的時候設置。並且方法里面也沒有提供其他可以改變字段內容的方法。
所以我們得出了一個震世驚俗的結論:record是immutable的。
record擴展
上面的例子中我們只使用了小括號里面的內容,大括號還是空的呀。可不可以像其他正常的類一樣,添加點方法或者構造函數進去呢?
答案是肯定的。
先看一個整體的方案:
public record CustUserWithBody(
String firstName,
String lastName,
Address address,
int age
) {
public String fullName(){
return firstName+ lastName;
}
public CustUserWithBody{
if (age < 18) {
throw new IllegalArgumentException( "男大當婚,女大當嫁,18歲未到,不許出嫁!");
}
}
}
我們在record的主題中,定義了一個方法和一個構造函數。
先看這個方法,在方法中我們可以訪問到record中定義的變量,但是千萬不要嘗試去修改他們,因為他們是final的,你會得到一個變異錯誤。
再看這個構造函數,這個構造函數沒有小括號,只有大括號,這種構造函數叫做Compact constructor。你無法在record中定義正常的構造函數,因為會得到一個編譯錯誤。
在這個Compact constructor中,我們可以對定義的字段進行數據校驗。如上所述。
總結
record是個好東西,希望能夠出現在JDK的正式版本中。
最后,很多人可能有個疑問,JDK14聲勢這么浩大,怎么感覺大的顛覆性的更新也沒有太多。
那么我以一個過來人的身份來回答一下這個問題:第一,JDK肯定要保持穩定,新特性都是次要的,穩定壓倒一切。所以6個月說長不長說短不短的時間里面注定不會有大的更新。第二,歐美公司的通病,在歐美公司工作不要太瀟灑,不但可以不打卡,在家上班,上班也是大大小小的會議開個不停,最后留下來寫程序的時間自然不會很多。牛人大神有很多,渾水摸魚的也不少。效率的話自然比不了國內的996+857了。
本文的例子https://github.com/ddean2009/learn-java-base-9-to-20
歡迎關注我的公眾號:程序那些事,更多精彩等着您!
更多內容請訪問 www.flydean.com