天天看點

Java 19和IntelliJ IDEA,如何和諧共生?

作者:慧都科技

Java仍然是目前比較流行的程式設計語言,它更短的釋出節奏讓開發者每六個月左右就可以試用新的語言或平台功能,IntelliJ IDEA幫助我們更流暢地發現和使用這些新功能。

複制連結擷取IntelliJ IDEA新版下載下傳(PC端):www.evget.com/product/2992/download

在本文中,我們将隻介紹Java 19的語言功能:記錄模式和switch模式比對(第三版預覽),特意避開其他Java 19功能,例如預覽API虛拟線程。IntelliJ IDEA支援虛拟線程的基本文法高亮顯示,官方團隊正努力在調試器和分析器中添加對虛拟線程的支援。

記錄模式簡化了對記錄元件的通路,比較記錄模式和記錄析構 – 當執行個體與記錄結構比對時,将記錄元件的值提取到一組變量,它與 switch 和密封類的模式比對等其他語言功能結合使用時,效果十分驚人。

switch 的模式比對将模式添加到 switch 語句和 switch 表達式中的 case 标簽,可以與 switch 一起使用的選擇器表達式的類型擴充為任意引用值。 另外case 标簽不再限于常量值,它還有助于将 if-else 語句鍊替換為 switch,提高代碼可讀性。 在這篇博文中,我們将介紹 switch 模式比對第三版預覽中引入的更改。

先從為使用 Java 19 功能對IntelliJ IDEA 進行配置開始。

Java 19和IntelliJ IDEA,如何和諧共生?

IntelliJ IDEA 配置

IntelliJ IDEA 2022.3 中提供了對 Java 19 的支援,未來的 IntelliJ IDEA 版本将提供更多支援。 要通過 Java 19 使用 switch 的模式比對,先轉到 Project Settings | Project(項目設定 | 項目),将 Project SDK(項目 SDK)設為 19,然後将項目語言級别設定為 19 (Preview) – Record patterns, pattern matching for switch (third preview):

Java 19和IntelliJ IDEA,如何和諧共生?

開發者可以使用系統上已經下載下傳的任意版本JDK,也可以點選 Edit(編輯),然後選擇 Add SDK >(添加 SDK)、Download JDK…(下載下傳 JDK…)來下載下傳其他版本,可以從供應商清單中選擇要下載下傳的 JDK 版本。

在 Modules(子產品)頁籤上,確定為子產品選擇相同的語言級别 – 19 (Preview) – Record patterns, pattern matching for switch (third preview):

Java 19和IntelliJ IDEA,如何和諧共生?

選擇此選項後,可能會出現以下彈出視窗,通知您 IntelliJ IDEA 可能會在後續版本中停止對 Java 預覽語言功能的支援。 因為預覽功能(暫且)不是永久性的,并且它可能在未來的 Java 版本中發生變化(甚至被移除)。

為什麼需要記錄模式?

資料是大多數應用程式的核心,通常開發者使用的應用程式可以查找資料,或者以幫助開發者做出決策的方式處理資料。當然如果應用程式無法存儲、檢索或處理其資料,這是不可行的。

在最近的一個 Java 版本(第 16 版)中,記錄被添加到 Java 語言中,讓開發者可以輕松處理資料。 記錄大幅簡化了對不可變資料模組化的方式,它們充當資料的透明載體或包裝器,隻需使用一行代碼,就可以定義一條記錄及其元件。

例如,以下單行代碼會建立一條新記錄 Person,後者可以存儲其元件 name 的字元串值和 age 的整數值:

record Person (String name, int age) { }           

記錄讓開發者不必些示例代碼,記錄會隐式生成其構造函數的預設實作、其元件的通路器方法,以及 toString、equals 和 hashCode 等效用函數方法。 使用記錄作為資料的包裝器時,您很可能需要将其展開來通路其元件。 例如,對于記錄 Person 的執行個體,可能想要檢查其年齡元件以确定它所代表的人是否有資格投票,isEligibleToVote 這樣的方法可以完成這個操作:

boolean isEligibleToVote(Object obj) {
if (obj instanceof Person person) {
return person.age() >= 18;
}
return false;
}           

前面的示例使用了 instanceof 的模式比對,它聲明了一個模式變量 person,是以您不需要建立局部變量來将 obj 轉換為 Person。

記錄模式更進一步。 它不僅将執行個體與記錄類型 Person 比較,還聲明了記錄元件的變量,是以無需定義局部變量或使用模式變量來通路記錄的元件,這都要歸功于編譯器知道記錄元件的确切數量和類型。

使用記錄模式重寫前面的方法,将記錄類型與 instanceof 運算符配合使用或在 switch case 标簽中使用時,IntelliJ IDEA可以檢測到它并建議使用記錄模式:

Java 19和IntelliJ IDEA,如何和諧共生?

這是修改後的代碼:

boolean isEligibleToVote(Object obj) {
if (obj instanceof Person(String name, int age)) {
return age >= 18;
}
return false;
}           

在前面的代碼中,記錄模式 Person(String name, int age) 似乎允許使用變量 age 代替 person.age()。 然而繼續閱讀,将了解記錄模式能夠簡化代碼的意圖,還有助于建立簡潔的資料處理代碼。

命名記錄模式

記錄模式後面可以跟随一個記錄模式變量,這種情況下,記錄模式被稱為命名記錄模式(雖然沒有得到确認,但 Java 20 的記錄模式的第二版預覽可能會放棄對命名記錄模式的支援)。

記錄模式還可以為其元件定義模式變量,用命名記錄模式并嘗試使用記錄模式變量通路其元件時,IntelliJ IDEA 會提示您為其元件使用模式變量,将看到此類代碼以黃色背景高亮顯示,可以使用 Alt+Enter 檢視建議,并接受修改代碼的建議:

Java 19和IntelliJ IDEA,如何和諧共生?

記錄模式和 null

我們回顧一下上一部分中的 isEligibleToVote 方法示例,如果将 null 值傳遞給以下方法會發生什麼:

boolean isEligibleToVote(Object obj) {
if (obj instanceof Person(String name, int age)) {
return age >= 18;
}
return false;
}           

由于 null 不是記錄模式 Person(String name, int age) 的執行個體,instanceof 運算符傳回 false,并且模式變量 name 和 age 未初始化。這很友善,因為記錄模式會處理null,開發者不需要定義非 null 檢查。

但是如果元件 name 的值為 null,則模式将被比對。

嵌套記錄模式 – 簡潔的代碼和明确的意圖

将另一條記錄定義為其元件的記錄相當常見,例如:

record Name (String fName, String lName) { }
record PhoneNumber(String areaCode, String number) { }
record Country (String countryCode, String countryName) { }
record Passenger (Name name,
PhoneNumber phoneNumber,
Country from,
Country destination) { }           

如果沒有可以檢查 null 元件值的記錄模式,您将需要幾個 null 檢查運算來處理記錄 Passenger 的 fName 和 countryCode 的元件值,如下所示:

boolean checkFirstNameAndCountryCode (Object obj) {
if (obj != null) {
if (obj instanceof Passenger passenger) {
Name name = null;
Country destination = null;

if (passenger.name() != null) {
name = passenger.name();

if (passenger.destination() != null) {
destination = passenger.destination();

String fName = name.fName();
String countryCode = destination.countryCode();

if (fName != null && countryCode != null) {
return fName.startsWith("Simo") &&
countryCode.equals("PRG");
}
}
}
}
}
return false;
}           

同樣的行為可以通過嵌套記錄模式實作,這也将使代碼的意圖更加清晰。 如果記錄元件 name 和 destination 為 null,instanceof 檢查将失敗:

boolean checkFirstNameAndCountryCodeAgain (Object obj) {
if (obj instanceof Passenger(Name (String fName, String lName),
PhoneNumber phoneNumber,
Country from,
Country (String countryCode, String countryName) )) {

if (fName != null && countryCode != null) {
return fName.startsWith("Simo") && countryCode.equals("PRG");
}
}
return false;
}           

如前面的示例代碼所示,開發者可以有選擇地添加主記錄元件的記錄模式。 例如前面的示例沒有為元件 from 使用記錄模式,但它為主記錄 Passenger 的記錄元件目标使用記錄模式。 簡而言之,定義記錄模式時,開發者可以控制要提取到模式變量的詳細資訊,這一功能非常适合資料處理密集型應用程式。

将var與記錄模式一起使用

來回顧一下前面示例中的方法 checkFirstNameAndCountryCodeAgain,并将一些模式變量的類型定義為 var:

boolean checkFirstNameAndCountryCodeAgain (Object obj) {
if (obj instanceof Passenger(Name (String fName, var lName),
var phoneNumber,
Country from,
Country (var countryCode, String countryName) )) {

if (fName != null && countryCode != null) {
return fName.startsWith("Simo") && countryCode.equals("PRG");
}
}
return false;
}           

開發者可以将部分或全部模式變量的類型定義為var,如果您好奇它們的類型,IntelliJ IDEA 可以顯示:

Java 19和IntelliJ IDEA,如何和諧共生?

記錄模式和泛型

如果記錄是泛型,則其記錄模式必須使用泛型類型。 例如假設類 WristWatch 和泛型記錄 Gift 的定義如下:

class WristWatch {}
record Gift<T>(T t) {}           

開發者可以使用以下方法解開記錄 Gift 的執行個體,可以使用 var 或 WristWatch 作為模式變量 watch 的類型:

void unwrap(Gift<WristWatch> obj) {
if (obj instanceof Gift<WristWatch> (var watch)) {
System.out.println(watch);
}
}           

但是,以下代碼将不起作用:

static void cannotUnwap(Gift<object> obj) {
if (obj instanceof Gift(var s)) { // won’t compile
//..
}
}           

下一部分使用記錄模式和 switch 表達式建立強大的遞歸方法。

記錄模式、switch 表達式和密封類

結合記錄模式、switch 表達式和密封類,開發者可以建立功能強大、簡潔且富有表現力的代碼來處理資料。 這是密封接口 TwoDimensional 的示例,它由記錄 Point、Line、Triangle 和 Square 實作:

sealed interface TwoDimensional {}
record Point (int x, int y) implements TwoDimensional { }
record Line ( Point start,
Point end) implements TwoDimensional { }
record Triangle( Point pointA,
Point pointB,
Point PointC) implements TwoDimensional { }
record Square ( Point pointA,
Point pointB,
Point PointC,
Point pointD) implements TwoDimensional { }           

下面的方法定義了一個遞歸方法程序,它使用 switch 構造傳回二維圖形(如 Line、Triangle 或 Square)中所有點的 x 和 y 坐标之和:

static int process(TwoDimensional twoDim) {
return switch (twoDim) {
case Point(int x, int y) -> x + y;
case Line(Point a, Point b) -> process(a) + process(b);
case Triangle(Point a, Point b, Point c) ->
process(a) + process(b) + process(c);
case Square(Point a, Point b, Point c, Point d) ->
process(a) + process(b) + process(c) + process(d);
};
}           

IntelliJ IDEA 還會在此方法的間距中顯示遞歸調用圖示:

Java 19和IntelliJ IDEA,如何和諧共生?

繼續閱讀