Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java 21の概要 / outline of Java 21

Java 21の概要 / outline of Java 21

2023/10/20に行われたJava 21リリースイベント@福岡での登壇資料です。
https://javaq.connpass.com/event/298600/

Naoki Kishida

October 20, 2023
Tweet

More Decks by Naoki Kishida

Other Decks in Programming

Transcript

  1. Java 21の概要
    2023-10-20 Java 21リリース記念イベント
    LINEヤフーコミュニケーションズ
    きしだ なおき
    オブジェクト指向からデータ指向プログラムへ

    View full-size slide

  2. 2023/10/20 2
    自己紹介

    きしだ なおき

    LINEヤフーコミュニケーションズ
    (旧LINE Fukuoka)

    twitter: @kis

    「プロになるJava」という
    Java入門書を書いてます

    View full-size slide

  3. 2023/10/20 3
    今日の話

    Java 21

    オブジェクト指向からデータ指向プログラミングへ

    View full-size slide

  4. Java 21

    9/19リリース

    LTS

    主な機能

    実用的なパターンマッチングが標準に!

    Record Patterns / Pattern Matching for switch

    Virtual Threadが標準に!

    パブリックスタティックヴォイドメインが簡潔に!(Preview)

    String Templatesで文字列に式を埋め込み(Preview)

    View full-size slide

  5. 2023/10/20 5
    用語

    JEP(JDK Enhancement Proposal)

    機能改善のまとめ

    Javaの主な仕様変更はJEPとしてまとめられている

    LTS(Long Term Support)

    長期サポート

    6バージョンごとにLTSが設定されていたけど、今回は4バージョンでの
    LTS

    LTS以外のバージョンは次のバージョンが出るとバグ修正などが行われ
    なくなる

    View full-size slide

  6. ディストリビューション

    OpenJDKをもとにいろいろなベンダーがディストリビューション
    を出している

    主なディストリビューション

    Oracle OpenJDK(jdk.java.netで配布されているバイナリ)

    Oracle JDK(Oracleのサイトで配布されている有償バイナリ)

    Eclipse Temurin

    クラウドベンダーが配布するディストリビューションも

    View full-size slide

  7. 試用機能

    大きな新機能について試用版を導入することでフィードバックを
    得やすくして、よりよい機能を提供できる

    言語機能: Preview

    API: Incubator

    JVM機能: Experimental

    View full-size slide

  8. 資料

    QiitaのJava21新機能まとめ

    https://qiita.com/nowokay/items/174f75b9e48cc7a80838

    はてなの記事

    https://nowokay.hatenablog.com/entry/2023/06/12/153755

    View full-size slide

  9. 2023/10/20 9
    Outline

    12 JEPs (1/2)

    Language

    JEP 430: String Templates(Preview)

    JEP 440: Record Patterns

    JEP 441: Pattern Matching for switch

    JEP 443: Unnamed Pattens and Variables(Preview)

    JEP 445: Unnamed Classes and Instance Main Methods(Preview)

    JVM

    JEP 439: Generational ZGC

    JEP 451: Prepare to Disallow the Dynamic Loading of Agents

    View full-size slide

  10. 2023/10/20 10
    Outline

    12 JEPs(2/2)

    API

    JEP 431: Sequenced Collections

    JEP 442: Foreign Function & Memory API(Third Preview)

    JEP 444: Virtual Threads

    JEP 446: Scoped Values(Preview)

    JEP 448: Vector API(Sixth Incubator)

    JEP 452: Key Encapsulation Mechanism API

    JEP 453: Structured Concurrency(Preview)

    JDK

    JEP 449: Deprecate the Windows 32-bit x86 Port for Removal

    View full-size slide

  11. 非互換性

    import間の余分なセミコロンがコンパイルエラーに

    仕様変更ではない

    javacのバグを修正して言語仕様どおりに
    import java.util.List;;
    import java.util.ArrayList;
    public class Test{
    }
    >javac Test.java
    Test.java:1: error: extraneous semicolon
    import java.util.List;;
    ^
    1 error
    import java.util.List;;
    public class Test{
    }
    OK, because semi-colons
    after import block are allowed

    View full-size slide

  12. Tooling in JShell

    JShellでjavacやjavapなどのJavaツールを利用

    add `TOOLING`
    C:\Users\naoki>jshell TOOLING
    | Welcome to JShell -- Version 21
    | For an introduction type: /help intro
    jshell> interface Empty{}
    | created interface Empty
    jshell> javap(Empty.class)
    Classfile /C:/Users/naoki/AppData/Local/Temp/TOOLING-17477864888169732169.class
    Last modified 2023/10/03; size 191 bytes
    SHA-256 checksum 1e53e9d7d4549a00361937701d3b0a613b520a68854310796db7879efc08d195
    Compiled from "$JShell$22.java"
    public interface REPL.$JShell$22$Empty
    minor version: 0
    major version: 65
    flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
    this_class: #1 // REPL/$JShell$22$Empty
    super_class: #3 // java/lang/Object
    interfaces: 0, fields: 0, methods: 0, attributes: 3

    View full-size slide

  13. 言語の変更

    正式機能

    Record Patterns

    Pattern Matching for switch

    Preview

    Unnamed Patterns and Variables

    String Templates

    Unnamed Classese and instance Main method

    View full-size slide

  14. 実用的なパターンマッチングが標準に!

    JEP 440 Record Patterns

    JEP 441 Pattern Matching for switch
    sealed interface Op permits Output, Goto {} // 命令
    record Output(String message) implements Op {}
    record Goto(int no) implements Op {}
    List codes = List.of( // プログラム
    new Output("hello"),
    new Output("world"),
    new Goto(0));
    int counter = 0;
    void clock() { // 命令実行
    counter++;
    switch(codes.get(counter-1)) { // 命令ごとの分岐
    case Output(var msg) -> System.out.println(msg);
    case Goto(var no) -> counter = no;
    }
    }

    View full-size slide

  15. Unnamed Patterns and variables(Preview)

    使わない変数やパターンを ‘_’として定義可能に

    JDK 22ではJEP 456として正式化されそう

    変数

    ローカル変数

    try-with-resource

    for

    拡張for

    catch

    ラムダ引数
    変数
    catch (InterruptedException _) {
    // do nothing
    }
    IntStream.range(0, 10)
    .forEach(_ -> System.out.println(“hello”))
    パターン
    case NegExpr(_) -> “negative”

    View full-size slide

  16. 文字列への式の埋め込み(JEP 430, Java 21)

    文字列に値を埋め込むのが面倒

    +での連結

    printfやString.format / formatted

    String Template

    STR."今日の温度は¥{temp}℃、¥{temp*9/5+32}℉です"

    SQLエスケープなどにも対応可

    SQL.“””
    select *
    from Product
    where name=”{name}”
    ”””

    View full-size slide

  17. String tempates(Preview)

    文字列補完

    STR.”Hello \{name}”

    JDK 22では2nd previewに
    ● JDK 21には3つのテンプレート。 STR, FMT, RAW
    jshell> STR."Today is \{new Date()} now"
    $1 ==> "Today is Wed Sep 20 05:34:20 JST 2023 now"
    jshell> import static java.util.FormatProcessor.FMT
    jshell> FMT."a=%05d\{a} d=%.3f\{d}"
    $9 ==> "a=00123 d=3.140"
    jshell> StringTemplate.RAW."a=\{a} d=\{d}"
    $11 ==> StringTemplate{ fragments = [ "a=", " d=", "" ], values = [123, 3.14] }

    View full-size slide

  18. カスタムテンプレート

    StringTemplate.Processorを実装

    View full-size slide

  19. パブリックスタティックヴォイドメインが簡潔に!

    Hello worldにいろんなキーワードが必要問題

    クラスの定義が不要に

    mainメソッドの引数を省略可能に

    インスタンスメソッドでもOK

    static不要

    publicじゃなくてもOK(privateだけダメ)

    説明に完全なコードを入れやすくなった

    →はWEB+DB PRESS 135の原稿

    Preview機能

    --enable-preview -source 21が必要

    View full-size slide

  20. 大きいプログラムから小さいプログラムへ

    Javaは大きなプログラムを確実に動かすことが主目的だった

    オブジェクト指向

    冗長な文法

    勉強や動作確認目的のコードが書きづらい

    みんなPythonを使ってしまう

    コンパイルが必要

    javaコマンドで直接javaファイルを実行可能に

    mainのためだけにたくさんの入力が必要

    クラスを省略可能に

    結果表示の文字列生成が面倒

    文字列補完

    View full-size slide

  21. API

    小さい変更

    Sequenced Collections

    Virtual Threads

    Scoped Values(Preview)

    Structured Concurrency(Preview)

    View full-size slide

  22. 小さい変更

    ranged indexOf


    Math.clamp


    isEmoji

    next page
    jshell> "test".indexOf('t', 1, 2)
    $20 ==> -1
    jshell> Math.clamp(1234, 10, 100)
    $13 ==> 100

    View full-size slide

  23. isEmoji

    絵文字かどうか判定


    多くの絵文字がサロゲートペア


    use `codePointAt`
    jshell> Character.isEmoji('A')
    $1 ==> false
    jshell> Character.isEmoji(' ')

    $2 ==> true
    jshell> Character.isEmoji(' ')
    🤗
    $3 ==> false
    jshell> Character.isEmoji(" ".codePointAt(0))
    🤗
    $8 ==> true

    View full-size slide

  24. Sequenced Collections

    List, SortedSet, Dequeは同じインタフェースを継承
    ● `SequencedCollection`

    void addFirst(E elm)

    void addLast(E elm)

    E getFirst()

    E getLast()

    E removeFirst()

    E removeLast()

    SequencedCollection reversed()

    View full-size slide

  25. Virtual Threadが標準に!

    いままでのスレッドは高機能

    しかしサーバーで複数リクエストを処理することが主な利用目的

    ネットワークの待ち時間が大きい

    スレッドは何もしていない

    無駄にリソースを食う

    OSスレッドを複数リクエストで使いまわすことが可能

    軽量化

    View full-size slide

  26. Virtual Threads

    JVM managed thread.

    OS managed threads are too heavy to reuse thread during waiting
    network
    IntStream.range(0, 100_000)
    .forEach(_ -> Thread.ofVirtual()
    .start(() -> sleep(ofSeconds(3))))
    IntStream.range(0, 100_000)
    .forEach(_ -> Thread.ofPlatform()
    .start(() -> sleep(ofSeconds(3))))

    View full-size slide

  27. Scoped Values(Preview)

    Sharing a value among methods on thread safe

    ThreadLocal is too heavy to the purpose
    String str;
    void start() {
    str = "test";
    proc1();
    }
    void proc1() {
    System.out.println(str);
    proc2();
    }
    void proc2() {
    System.out.println(str);
    }
    final ScopedValue STR = new  ScopedValue<>();
    void start() {
    ScopedValue.where(STR, "test")
    .run(() -> proc1());
    }
    void proc1() {
    System.out.println(STR.get());
    proc2();
    }
    void proc2() {
    System.out.println(STR.get());
    }

    View full-size slide

  28. Structured Concurrency(Preview)

    When building single unit of task by different thread,
    synchronizing the subtask is required. But using `wait` and `join`
    will make “go-to hell” like complexity.

    Structured concurrency preserves the natural relationship
    between tasks and subtasks.
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    Future user = scope.fork(() -> findUser());
    Future order = scope.fork(() -> fetchOrder());
    scope.join(); // Join both forks
    scope.throwIfFailed(); // ... and propagate errors
    // Here, both forks have succeeded, so compose their results
    return new Response(user.resultNow(), order.resultNow());
    }

    View full-size slide

  29. オブジェクト指向からデータ指向プログラミングへ

    オブジェクト指向はプログラムのモジュール化の技法

    すでにマイクロサービスなどでプログラムは小さくなっている

    プログラムをモジュールに分割する必要がない

    データ指向プログラミング

    データをデータとして扱う

    データを扱う場合にはデータがどのような属性をもつかという
    型の定義が大切

    抽象データ型と代数的データ型

    View full-size slide

  30. クラスの分類

    クラスの役割を分類する

    クラスには型とモジュールの性質がある
    分類 役割 性質 備考
    システム境界 入出力など外部とのやりとり モジュール+型 オブジェクト指向がむい
    ている。しかしフレーム
    ワークなどがすでに提供
    データ データを保持 型
    処理 手続きを記述 モジュール 型として扱うことはない

    View full-size slide

  31. 抽象データ型と代数的データ型

    抽象データ型

    型の定義の指針

    代数的データ型

    型の組み合わせの指針

    View full-size slide

  32. 抽象データ型

    データの操作だけ公開することで変更に強く柔軟な型を定義

    データ特化の「カプセル化」

    操作としてデータ操作だけを含む
    class PersonName {
    PersonName (String surname, String givenName);
    String getFullName();
    String getSurname();
    String getGivenName();
    }
    class PersonName {
    private String surname;
    private String givenName;
    PersonName (String surname, String givenName) {
    this.surname = surname;
    this.givenName = givenName;
    }
    String getFullName() {
    return "%s %s".formatted(surname, givenName);
    }
    ...
    class PersonName {
    private String fullName;
    PersonName (String surname, String givenName) {
    this.fullName = surname + " " + givenName;
    }
    String getFullName() {
    return fullName;
    }
    String getSurname() {
    return fullName.split(" ")[0];
    }
    ...
    どちらでもOK

    View full-size slide

  33. レコードクラス

    データを定義するための型

    record Person(String name, int age) {}

    他の型を継承することも、他の型で継承することもできない

    インタフェースの実装は可能

    View full-size slide

  34. データ型のルール

    純粋関数をデータ型に拡張する

    純粋データ型?

    参照透過

    戻り値が引数だけで決まる

    副作用なし

    状態の変更や外部への入出力なし

    参照透過

    戻り値が引数とフィールドだけで決まる

    副作用なし

    フィールド変更以外の状態の変更や
    外部への入出力なし

    View full-size slide

  35. 代数的データ型

    直積型

    クラス

    配列

    レコードクラス

    直和型

    継承

    sealed

    View full-size slide

  36. 直積型

    値を組み合わせる型

    レコードクラスは直積型を実現するために導入されたと考えれる

    値のとりうるパターンがそれぞれの値のパターン数の積になる

    record A(boolean a1, byte a2) {}

    2 x 256で512とおり

    View full-size slide

  37. 直和型

    いずれかの型になる

    catch (IOException | InterruptedException ex)

    IOExceptionかInterruptedExcdeption

    java.nio.Buffer

    IntBufferかByteBufferか・・・

    View full-size slide

  38. Sealed型

    java.nio.Bufferのように継承を使う場合

    コンストラクタをパッケージプライベートにすると限定できる

    javaパッケージであれば新たに追加されることはない

    アプリケーションクラスではJARをあとから追加が可能

    可能な型を限定できない

    sealed型で限定 sealed interface Type {
    record Bulk(int price, int unit) implements Type {}
    record Packed(int price) implements Type {}
    }
    sealed interface Type permits Bulk, Packed {}
    record Bulk(int price, int unit) implements Type {}
    record Packed(int price) implements Type {}

    View full-size slide

  39. データの処理を考える

    こんな値を合計する
    record Product(String name, Type type) {}
    record Item(Product product, int amount) {}
    var cart = List.of(
    new Item(new Product("餃子", new Packed(300)), 3),
    new Item(new Product("牛肉", new Bulk(250, 100)), 230));

    View full-size slide

  40. Streamで書いてみる

    次のようになる
    int total = cart.stream()
    .mapToInt(item -> {
    int amount = item.amount();
    Type type = item.product().type();
    if (type instanceof Bulk) {
    Bulk b = (Bulk)type;
    return b.price() * item.amount() / b.unit();
    } else if(type instanceof Packed) {
    Packed p = (Packed)type;
    return p.price() * item.amount();
    } else {
    throw new IllegalArgumentException();
    }
    }).sum();

    View full-size slide

  41. パターンマッチ for instanceof

    instanceofとキャストがダサい

    パターンマッチの導入
    if (type instanceof Bulk) {
    Bulk b = (Bulk)type;
    return b.price() * item.amount() / b.unit();
    }
    if (type instanceof Bulk b) {
    return b.price() * item.amount() / b.unit();
    }

    View full-size slide

  42. レーコードパターン

    パターンマッチでレコードの分解
    if (type instanceof Bulk b) {
    return b.price() * item.amount() / b.unit();
    }
    if (type instanceof Bulk(int price, int unit) {
    return price * item.amount() / unit;
    }

    View full-size slide

  43. Switchでのパターンマッチング
    if (type instanceof Bulk) {
    Bulk b = (Bulk)type;
    return b.price() * item.amount() / b.unit();
    } else if(type instanceof Packed) {
    Packed p = (Packed)type;
    return p.price() * item.amount();
    } else {
    throw new IllegalArgumentException();
    }
    switch (type)
    case Bulk(int price, int unit) -> {
    return b.price() * item.amount() / b.unit();
    }
    case Packed p -> {
    return p.price() * item.amount();
    }
    }
    ※ sealedで型が限定されているのでdefault不要

    View full-size slide

  44. Switch式

    Java 14で正式導入
    int total = cart.stream()
    .mapToInt(item -> switch(item) {
    case Item(Product(var n,
    Packed(int price)), int amount)
    -> price * amount;
    case Item(Product(var n,
    Bulk(int price, int unit)), int amount)
    -> price * amount / unit;
    }).sum();

    View full-size slide

  45. 継承で実装してみる

    継承での実装
    sealed interface Type {
    /** 量り売り */
    record Bulk(int price, int unit) implements Type {
    int calc(int amount) {
    return price * amount / unit;
    }
    }
    /** パッケージ */
    record Packed(int price) implements Type {
    int calc(int amount) {
    return price * amount;
    }
    }
    int calc(int amount);
    }
    int total = cart.stream()
    .mapToInt(item ->
    item.product().type().calc(item.amount()))
    .sum();

    View full-size slide

  46. 継承のほうがいい?

    すっきりして見える
    int total = cart.stream()
    .mapToInt(item ->
    item.product().type().calc(item.amount()))
    .sum();

    View full-size slide

  47. 継承で実現したときの欠点

    追うときにいろいろなクラスを見る必要がある

    他のデータが関係あるときに対応できない

    1パック170円、3パック500円とか

    場合わけの型が2つあるときれいにかけない

    商品種別と時間帯とか

    どちらかの型に場合わけが必要

    業務処理では一箇所にすべての型の処理をまとめるほうがいい

    システム境界では他の型が関係することがすくないので継承が有効

    OracleConnectionがMySQLConnectionを気にしたりとかはない

    View full-size slide

  48. 書籍

    WEB+DB PRESS vol.134

    データ指向プログラミング

    View full-size slide