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. 2023/10/20 2 自己紹介 • きしだ なおき • LINEヤフーコミュニケーションズ (旧LINE Fukuoka)

    • twitter: @kis • 「プロになるJava」という Java入門書を書いてます
  2. Java 21 • 9/19リリース • LTS • 主な機能 • 実用的なパターンマッチングが標準に!

    • Record Patterns / Pattern Matching for switch • Virtual Threadが標準に! • パブリックスタティックヴォイドメインが簡潔に!(Preview) • String Templatesで文字列に式を埋め込み(Preview)
  3. 2023/10/20 5 用語 • JEP(JDK Enhancement Proposal) • 機能改善のまとめ •

    Javaの主な仕様変更はJEPとしてまとめられている • LTS(Long Term Support) • 長期サポート • 6バージョンごとにLTSが設定されていたけど、今回は4バージョンでの LTS • LTS以外のバージョンは次のバージョンが出るとバグ修正などが行われ なくなる
  4. ディストリビューション • OpenJDKをもとにいろいろなベンダーがディストリビューション を出している • 主なディストリビューション • Oracle OpenJDK(jdk.java.netで配布されているバイナリ) •

    Oracle JDK(Oracleのサイトで配布されている有償バイナリ) • Eclipse Temurin • クラウドベンダーが配布するディストリビューションも
  5. 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
  6. 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
  7. 非互換性 • 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
  8. 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
  9. 言語の変更 • 正式機能 • Record Patterns • Pattern Matching for

    switch • Preview • Unnamed Patterns and Variables • String Templates • Unnamed Classese and instance Main method
  10. 実用的なパターンマッチングが標準に! • 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<Op> 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; } }
  11. 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”
  12. 文字列への式の埋め込み(JEP 430, Java 21) • 文字列に値を埋め込むのが面倒 • +での連結 • printfやString.format

    / formatted • String Template • STR."今日の温度は¥{temp}℃、¥{temp*9/5+32}℉です" • SQLエスケープなどにも対応可 • SQL.“”” select * from Product where name=”{name}” ”””
  13. 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] }
  14. パブリックスタティックヴォイドメインが簡潔に! • Hello worldにいろんなキーワードが必要問題 • クラスの定義が不要に • mainメソッドの引数を省略可能に • インスタンスメソッドでもOK

    • static不要 • publicじゃなくてもOK(privateだけダメ) • 説明に完全なコードを入れやすくなった • →はWEB+DB PRESS 135の原稿 • Preview機能 • --enable-preview -source 21が必要
  15. 大きいプログラムから小さいプログラムへ • Javaは大きなプログラムを確実に動かすことが主目的だった • オブジェクト指向 • 冗長な文法 • 勉強や動作確認目的のコードが書きづらい •

    みんなPythonを使ってしまう • コンパイルが必要 • javaコマンドで直接javaファイルを実行可能に • mainのためだけにたくさんの入力が必要 • クラスを省略可能に • 結果表示の文字列生成が面倒 • 文字列補完
  16. API • 小さい変更 • Sequenced Collections • Virtual Threads •

    Scoped Values(Preview) • Structured Concurrency(Preview)
  17. 小さい変更 • ranged indexOf • • Math.clamp • • isEmoji

    • next page jshell> "test".indexOf('t', 1, 2) $20 ==> -1 jshell> Math.clamp(1234, 10, 100) $13 ==> 100
  18. 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
  19. Sequenced Collections • List, SortedSet, Dequeは同じインタフェースを継承 • `SequencedCollection<E>` • void

    addFirst(E elm) • void addLast(E elm) • E getFirst() • E getLast() • E removeFirst() • E removeLast() • SequencedCollection<E> reversed()
  20. 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))))
  21. 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<String> 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()); }
  22. 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<String> user = scope.fork(() -> findUser()); Future<Integer> 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()); }
  23. クラスの分類 • クラスの役割を分類する • クラスには型とモジュールの性質がある 分類 役割 性質 備考 システム境界

    入出力など外部とのやりとり モジュール+型 オブジェクト指向がむい ている。しかしフレーム ワークなどがすでに提供 データ データを保持 型 処理 手続きを記述 モジュール 型として扱うことはない
  24. 抽象データ型 • データの操作だけ公開することで変更に強く柔軟な型を定義 • データ特化の「カプセル化」 • 操作としてデータ操作だけを含む 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
  25. レコードクラス • データを定義するための型 • record Person(String name, int age) {}

    • 他の型を継承することも、他の型で継承することもできない • インタフェースの実装は可能
  26. データ型のルール • 純粋関数をデータ型に拡張する • 純粋データ型? • 参照透過 • 戻り値が引数だけで決まる •

    副作用なし • 状態の変更や外部への入出力なし • 参照透過 • 戻り値が引数とフィールドだけで決まる • 副作用なし • フィールド変更以外の状態の変更や 外部への入出力なし
  27. 直和型 • いずれかの型になる • catch (IOException | InterruptedException ex) •

    IOExceptionかInterruptedExcdeption • java.nio.Buffer • IntBufferかByteBufferか・・・
  28. 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 {}
  29. データの処理を考える • こんな値を合計する 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));
  30. 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();
  31. パターンマッチ 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(); }
  32. レーコードパターン • パターンマッチでレコードの分解 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; }
  33. 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不要
  34. 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();
  35. 継承で実装してみる • 継承での実装 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();
  36. 継承で実現したときの欠点 • 追うときにいろいろなクラスを見る必要がある • 他のデータが関係あるときに対応できない • 1パック170円、3パック500円とか • 場合わけの型が2つあるときれいにかけない •

    商品種別と時間帯とか • どちらかの型に場合わけが必要 • 業務処理では一箇所にすべての型の処理をまとめるほうがいい • システム境界では他の型が関係することがすくないので継承が有効 • OracleConnectionがMySQLConnectionを気にしたりとかはない