Java SE 23 新增特性

Grey Zeng發表於2024-09-19

Java SE 23 新增特性

作者:Grey

原文地址:

部落格園:Java SE 23 新增特性

CSDN:Java SE 23 新增特性

原始碼

源倉庫: Github:java_new_features

Primitive Types in Patterns, instanceof, and switch (預覽功能)

透過 instanceof 和 switch,我們可以檢查物件是否屬於特定型別,如果是,則將該物件繫結到該型別的變數,執行特定的程式路徑,並在該程式路徑中使用新變數。

public class PrimitiveTypesTest {
    void main() {
        test1("hello world");
        test2("hello world");
        test1(56);
        test2(56);
        test1(java.time.LocalDate.now());
        test2(java.time.LocalDate.now());
    }

    private static void test1(Object obj) {
        if (obj instanceof String s && s.length() >= 5) {
            System.out.println(s.toUpperCase());
        } else if (obj instanceof Integer i) {
            System.out.println(i * i);
        } else {
            System.out.println(obj);
        }
    }

    private static void test2(Object obj) {
        switch (obj) {
            case String s when s.length() >= 5 -> System.out.println(s.toUpperCase());
            case Integer i -> System.out.println(i * i);
            case null, default -> System.out.println(obj);
        }
    }
}

JEP 455 在 Java 23 中引入了兩項變更:

  • 可以在 switch 表示式和語句中使用所有基元型別,包括 long、float、double 和 boolean。

  • 其次,我們還可以在模式匹配中使用所有基元型別,包括 instanceof 和 switch。

在這兩種情況下,即透過 long、float、double 和布林型別進行 switch 以及使用基元變數進行模式匹配時,與所有新的 switch 功能一樣,switch 必須要涵蓋所有可能的情況。

private static void test3(int x) {
    switch (x) {
        case 1, 2, 3 -> System.out.println("Low");
        case 4, 5, 6 -> System.out.println("Medium");
        case 7, 8, 9 -> System.out.println("High");
    }
}

Module Import Declarations (模組匯入宣告,預覽功能)

透過簡潔地匯入模組匯出的所有包的功能來增強 Java 程式語言。這簡化了模組庫的重複使用,但不要求匯入程式碼本身必須在模組中。這是一項預覽語言功能。

自 Java 1.0 起,java.lang 包中的所有類都會自動匯入到每個 .java 檔案中。這就是為什麼我們無需匯入語句就能使用 ObjectStringIntegerExceptionThread 等類的原因。

我們還可以匯入完整的包。例如,匯入 java.util.* 意味著我們不必單獨匯入 ListSetMapArrayListHashSetHashMap 等類。

JEP 467現在允許我們匯入完整的模組,更準確地說,是匯入模組匯出的包中的所有類。

例如,我們可以按如下方式匯入完整的 java.base 模組,然後使用該模組中的類(例如 ListMapCollectorsStream),而無需進一步匯入:

package git.snippets.jdk23;

import module java.base;

public class ModuleImportDeclarationsTest {
    void main() {
        System.out.println(groupByFirstLetter("a", "abc", "bcd", "ddd", "dddc", "dfc", "bc"));
    }

    public static Map<Character, List<String>> groupByFirstLetter(String... values) {
        return Stream.of(values).collect(Collectors.groupingBy(s -> Character.toUpperCase(s.charAt(0))));
    }
}

如果有兩個同名的匯入類,例如下面示例中的 Date,編譯器就會出錯:

import module java.base;
import module java.sql;

如果一個匯入模組臨時匯入了另一個模組,那麼我們也可以使用臨時匯入模組匯出包中的所有類,而無需顯式匯入。

例如,java.sql 模組轉義匯入了 java.xml 模組:

module java.sql {
  . . .
  requires transitive java.xml;
  . . .
}

因此,在下面的示例中,我們不需要顯式匯入 SAXParserFactory 和 SAXParser,也不需要顯式匯入 java.xml 模組:

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();

Flexible Constructor Bodies (二次預覽)

在 JDK 23 之前,下述程式碼中,Child1的建構函式,只能先透過super構造父類,然後才能初始化子類的 b 這個變數。

public class FlexibleConstructorBodies {
    void main() {
        new Child1(1, 2);
    }
}


class Parent {
    private final int a;

    public Parent(int a) {
        this.a = a;
        printMe();
    }

    void printMe() {
        System.out.println("a = " + a);
    }
}

// JDK 23 之前
class Child1 extends Parent {
    private final int b;

    public Child1(int a, int b) {
        super(verifyParamsAndReturnA(a, b));
        this.b = b;
    }

    @Override
    void printMe() {
        super.printMe();
        System.out.println("b = " + b);
    }

    private static int verifyParamsAndReturnA(int a, int b) {
        if (a < 0 || b < 0) throw new IllegalArgumentException();
        return a;
    }
}

當我們執行

new Child1(1,2);

這段程式碼的時候,本來我們期待返回的是

a = 1
b = 2

但是由於父類在構造時候呼叫了printMe(),且這個呼叫是在 b 變數初始化之前呼叫的,所以導致程式執行的結果是

a = 1
b = 0

今後,在使用 super(...) 呼叫超級建構函式之前,以及在使用 this(...) 呼叫替代建構函式之前,我們可以執行任何不訪問當前構造例項(即不訪問其欄位)的程式碼

此外,我們還可以初始化正在構造的例項的欄位。詳見JEP 482

在 JDK 23 上,上述程式碼可以調整為:

class Child2 extends Parent {
    private final int b;

    public Child2(int a, int b) {
        if (a < 0 || b < 0) throw new IllegalArgumentException();  // ⟵ Now allowed!
        this.b = b;                                                // ⟵ Now allowed!
        super(a);
    }

    @Override
    void printMe() {
        super.printMe();
        System.out.println("b = " + b);
    }
}

其中建構函式中,a和b的初始化和判斷,都可以在super(...)函式呼叫之前,
執行

new Child2(1,2);

列印結果為預期結果

a = 1
b = 2

Implicitly Declared Classes and Instance Main Methods (第三次預覽)

最早出現在 JDK 21 中,見Java SE 21 新增特性

原來我們寫一個main方法,需要

public class UnnamedClassesAndInstanceMainMethodsTest {

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

}

而且Java檔案的名稱需要和UnnamedClassesAndInstanceMainMethodsTest保持一致,到了JDK 23,上述程式碼可以簡化成

void main() {
    System.out.println("hello world");
}

甚至連 public class ... 這段也不需要寫。

更多

Java SE 7及以後各版本新增特性,持續更新中...

參考資料

Java Language Changes for Java SE 23

JDK 23 Release Notes

JAVA 23 FEATURES(WITH EXAMPLES

相關文章