Java原生支持Lombok了,你知道嗎?
你好,我是看山。
我們在開發(fā)時會借助Lombok快速填充POJO類的模板方法,比如:getter、setter、equals、hashCode等,引入這個組件的原因是方便,一個注解就可以代理一堆的模板方法。
從Java16開始,我們可以借助Record類型實現(xiàn)相同的功能了,接下來,我們詳細(xì)看下這個類型。
Record是Java中的一種特殊類,旨在為程序員提供一種高效且簡便的方式實現(xiàn)POJO類。Record類型通過關(guān)鍵字record實現(xiàn)。
一、Java中的Record類是什么?
在Java項目開發(fā)中,作為開發(fā)者,我們常常編寫服務(wù)類、安全類或其他基礎(chǔ)類,這些類本質(zhì)上是功能性的。同樣,程序員也經(jīng)常編寫僅用于承載數(shù)據(jù)的類,即POJO類。
例如,假設(shè)客戶端向服務(wù)器請求某個人的“id”和“name”等數(shù)據(jù),服務(wù)器會以相應(yīng)的數(shù)據(jù)進(jìn)行響應(yīng)。
由于Java中一切皆為對象,所以必然存在某個類來承載這些數(shù)據(jù),服務(wù)器會將該類的對象返回給客戶端,該對象的唯一目的就是將數(shù)據(jù)從服務(wù)器傳遞到客戶端。
然而,編寫這樣一個數(shù)據(jù)類,即便它可能只是一個簡單的POJO,也會包含大量的模板代碼,比如私有字段、構(gòu)造函數(shù)、getter和setter方法、hashCode()、equals()和toString()方法。
由于Java語言的冗長特性,一個簡單的承載類會因大量不必要的代碼而變得臃腫。
這些弊端促使了一種特殊類型的類Record的誕生。該類能夠聚合(或持有)一組值,無需編寫自動生成樣板代碼,可以說是一種高效的數(shù)據(jù)對象構(gòu)建方式。
雖然,即使沒有Record類型,我們也能寫好代碼,比如借助IDEA的模板生成,比如借助Lombok的注解,但是,原生支持的能力,在便利性和效率方面,可以給代碼注入新的靈魂。
我們看下以前的寫法:
import java.util.Objects;
public class Person {
private int id;
private String name;
public Person() {}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass()!= o.getClass()) return false;
Person person = (Person) o;
return getId() == person.getId() && getName().equals(person.getName());
}
@Override
public int hashCode() {
return Objects.hash(getId(), getName());
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
是不是看著都不想寫了。
二、如何在Java中創(chuàng)建Record類?
Java中的Record類由關(guān)鍵字record支持,語法為:
record recordName(list-of-components) {
//可選語句
}
以record開頭,后面跟著類名和參數(shù)列表。此時,我們可以把前面的Person改寫為:
record Person(int id, String name){}
一下子就清爽了。
使用起來和原來沒有太多差異:
Person p1 = new Person(1, "Peter Parker");
在編譯過程中,編譯器會自動提供存儲數(shù)據(jù)所需的元素、構(gòu)造函數(shù)、訪問數(shù)據(jù)的getter方法、toString()、equals()和hashCode()方法。因此,雖然沒有寫下面這些方法,也能夠正常使用。
Person p1 = new Person(1, "Peter Parker");
Person p2 = new Person(2, "Spiderman");
System.out.println(p1.toString());
System.out.println(p1.equals(p2));
System.out.println(p1.name());
關(guān)于上述代碼示例,有以下幾個要點需要注意:
- Record類提供的規(guī)范構(gòu)造函數(shù)包含與組件列表中傳遞的相同參數(shù),且順序一致。傳遞的值會自動賦給記錄字段。
- Record類通過“new”關(guān)鍵字進(jìn)行實例化,就像在Java中創(chuàng)建其他任何對象一樣。
- Record類中的數(shù)據(jù)保存在私有final字段中,并且只有g(shù)etter方法。因此,記錄中的數(shù)據(jù)是不可變的。
- Record類不能繼承其他類。是因為已經(jīng)隱式繼承java.lang.Record。并重寫了Object類的equals()、hashCode()和toString()方法。
- Record類聲明都是final的,所以它們不能被擴展。
- Record類可以實現(xiàn)一個或多個接口。
- 除了聲明的參數(shù)外,任何其他字段必須聲明為靜態(tài)的。
三、Java中的規(guī)范構(gòu)造函數(shù)
在Record中,如果需要定義規(guī)范構(gòu)造函數(shù),需要按照預(yù)設(shè)格式定義。我們有兩種方式來聲明我們自己的實現(xiàn)。
方式一、常用的規(guī)范構(gòu)造函數(shù):
record Invoice(String id, float amount) {
static String prefix = String.valueOf(Calendar.getInstance().get(Calendar.YEAR))
+ String.valueOf(Calendar.getInstance().get(Calendar.MONTH) + 1);
public Invoice(String id, float amount) {
this.id = prefix + id.trim();
this.amount = amount;
}
}
方式二、緊湊構(gòu)造函數(shù)。在緊湊構(gòu)造函數(shù)中,簽名聲明是隱式的。我們只需提供Record類名作為構(gòu)造函數(shù),不帶任何參數(shù)。這種類型的構(gòu)造函數(shù)具有所有參數(shù)的隱式聲明,并且會自動將傳遞給記錄組件的值賦給相應(yīng)參數(shù)。此外,在緊湊構(gòu)造函數(shù)中,不使用this關(guān)鍵字。
record Invoice(String id, float amount) {
static String prefix = String.valueOf(Calendar.getInstance().get(Calendar.YEAR))
+ String.valueOf(Calendar.getInstance().get(Calendar.MONTH) + 1);
public Invoice {
id = prefix + id.trim();
amount = amount;
}
}
四、Java中的非規(guī)范構(gòu)造函數(shù)
除了規(guī)范構(gòu)造函數(shù),我們還可以定義非規(guī)范構(gòu)造函數(shù)。比如:當(dāng)我們只想用默認(rèn)初始化一個字段,此時,我們可以編寫一個非規(guī)范構(gòu)造函數(shù)。
非規(guī)范構(gòu)造函數(shù)的關(guān)鍵要求是,構(gòu)造函數(shù)必須使用this關(guān)鍵字調(diào)用記錄中的另一個規(guī)范構(gòu)造函數(shù)。
以下是一個簡單示例:
record Invoice(String id, float amount) {
static String prefix = String.valueOf(Calendar.getInstance().get(Calendar.YEAR))
+ String.valueOf(Calendar.getInstance().get(Calendar.MONTH) + 1);
public Invoice {
id = prefix + id.trim();
amount = amount;
}
public Invoice(String id) {
this(id, 0.0f);
}
}
非規(guī)范構(gòu)造函數(shù)就像是預(yù)設(shè)默認(rèn)值的一種實現(xiàn)。
五、Java Record類代碼示例
最后,我們來個示例代碼,展示了如何在Java中使用Record類、規(guī)范構(gòu)造函數(shù)和非規(guī)范構(gòu)造函數(shù):
record Invoice(String id, float amount) {
static String prefix = String.valueOf(Calendar.getInstance().get(Calendar.YEAR))
+ String.valueOf(Calendar.getInstance().get(Calendar.MONTH) + 1);
public Invoice {
id = prefix + id.trim();
if (amount < 0)
throw new IllegalArgumentException("-ve values not allowed");
amount = amount;
}
public Invoice(String id) {
this(id, 0.0f);
}
}
public class App {
public static void main(String[] args) {
float[] amt = {400.00f, 600.00f, 300.00f, 700.00f, 600.00f};
Invoice[] invoice = new Invoice[5];
for (int i = 0; i < invoice.length; i++)
invoice[i] = new Invoice(String.valueOf(i + 1), amt[i]);
for (int i = 0; i < invoice.length; i++)
System.out.println(invoice[i].toString());
}
}
文末總結(jié)
Record類和record關(guān)鍵字的引入,極大地提升了定義類的便捷性,打破了Java語言規(guī)范中POJO類冗長的聲明。我們可以不用他,但是要知道其用法。就像是Java8的stream一樣,Record類也有很大魅力。