Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

2023/10/20 2 自己紹介 ● きしだ なおき ● LINEヤフーコミュニケーションズ (旧LINE Fukuoka) ● twitter: @kis ● 「プロになるJava」という Java入門書を書いてます

Slide 3

Slide 3 text

2023/10/20 3 今日の話 ● Java 21 ● オブジェクト指向からデータ指向プログラミングへ

Slide 4

Slide 4 text

Java 21 ● 9/19リリース ● LTS ● 主な機能 ● 実用的なパターンマッチングが標準に! ● Record Patterns / Pattern Matching for switch ● Virtual Threadが標準に! ● パブリックスタティックヴォイドメインが簡潔に!(Preview) ● String Templatesで文字列に式を埋め込み(Preview)

Slide 5

Slide 5 text

2023/10/20 5 用語 ● JEP(JDK Enhancement Proposal) ● 機能改善のまとめ ● Javaの主な仕様変更はJEPとしてまとめられている ● LTS(Long Term Support) ● 長期サポート ● 6バージョンごとにLTSが設定されていたけど、今回は4バージョンでの LTS ● LTS以外のバージョンは次のバージョンが出るとバグ修正などが行われ なくなる

Slide 6

Slide 6 text

ディストリビューション ● OpenJDKをもとにいろいろなベンダーがディストリビューション を出している ● 主なディストリビューション ● Oracle OpenJDK(jdk.java.netで配布されているバイナリ) ● Oracle JDK(Oracleのサイトで配布されている有償バイナリ) ● Eclipse Temurin ● クラウドベンダーが配布するディストリビューションも

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

資料 ● QiitaのJava21新機能まとめ ● https://qiita.com/nowokay/items/174f75b9e48cc7a80838 ● はてなの記事 ● https://nowokay.hatenablog.com/entry/2023/06/12/153755 ●

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

非互換性 ● 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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

言語の変更 ● 正式機能 ● Record Patterns ● Pattern Matching for switch ● Preview ● Unnamed Patterns and Variables ● String Templates ● Unnamed Classese and instance Main method

Slide 14

Slide 14 text

実用的なパターンマッチングが標準に! ● 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; } }

Slide 15

Slide 15 text

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”

Slide 16

Slide 16 text

文字列への式の埋め込み(JEP 430, Java 21) ● 文字列に値を埋め込むのが面倒 ● +での連結 ● printfやString.format / formatted ● String Template ● STR."今日の温度は¥{temp}℃、¥{temp*9/5+32}℉です" ● SQLエスケープなどにも対応可 ● SQL.“”” select * from Product where name=”{name}” ”””

Slide 17

Slide 17 text

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] }

Slide 18

Slide 18 text

カスタムテンプレート ● StringTemplate.Processorを実装

Slide 19

Slide 19 text

パブリックスタティックヴォイドメインが簡潔に! ● Hello worldにいろんなキーワードが必要問題 ● クラスの定義が不要に ● mainメソッドの引数を省略可能に ● インスタンスメソッドでもOK ● static不要 ● publicじゃなくてもOK(privateだけダメ) ● 説明に完全なコードを入れやすくなった ● →はWEB+DB PRESS 135の原稿 ● Preview機能 ● --enable-preview -source 21が必要

Slide 20

Slide 20 text

大きいプログラムから小さいプログラムへ ● Javaは大きなプログラムを確実に動かすことが主目的だった ● オブジェクト指向 ● 冗長な文法 ● 勉強や動作確認目的のコードが書きづらい ● みんなPythonを使ってしまう ● コンパイルが必要 ● javaコマンドで直接javaファイルを実行可能に ● mainのためだけにたくさんの入力が必要 ● クラスを省略可能に ● 結果表示の文字列生成が面倒 ● 文字列補完

Slide 21

Slide 21 text

API ● 小さい変更 ● Sequenced Collections ● Virtual Threads ● Scoped Values(Preview) ● Structured Concurrency(Preview)

Slide 22

Slide 22 text

小さい変更 ● ranged indexOf ● ● Math.clamp ● ● isEmoji ● next page jshell> "test".indexOf('t', 1, 2) $20 ==> -1 jshell> Math.clamp(1234, 10, 100) $13 ==> 100

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Sequenced Collections ● List, SortedSet, Dequeは同じインタフェースを継承 ● `SequencedCollection` ● void addFirst(E elm) ● void addLast(E elm) ● E getFirst() ● E getLast() ● E removeFirst() ● E removeLast() ● SequencedCollection reversed()

Slide 25

Slide 25 text

Virtual Threadが標準に! ● いままでのスレッドは高機能 ● しかしサーバーで複数リクエストを処理することが主な利用目的 ● ネットワークの待ち時間が大きい ● スレッドは何もしていない ● 無駄にリソースを食う ● OSスレッドを複数リクエストで使いまわすことが可能 ● 軽量化

Slide 26

Slide 26 text

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))))

Slide 27

Slide 27 text

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()); }

Slide 28

Slide 28 text

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()); }

Slide 29

Slide 29 text

オブジェクト指向からデータ指向プログラミングへ ● オブジェクト指向はプログラムのモジュール化の技法 ● すでにマイクロサービスなどでプログラムは小さくなっている ● プログラムをモジュールに分割する必要がない ● データ指向プログラミング ● データをデータとして扱う ● データを扱う場合にはデータがどのような属性をもつかという 型の定義が大切 ● 抽象データ型と代数的データ型

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

抽象データ型と代数的データ型 ● 抽象データ型 ● 型の定義の指針 ● 代数的データ型 ● 型の組み合わせの指針

Slide 32

Slide 32 text

抽象データ型 ● データの操作だけ公開することで変更に強く柔軟な型を定義 ● データ特化の「カプセル化」 ● 操作としてデータ操作だけを含む 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

Slide 33

Slide 33 text

レコードクラス ● データを定義するための型 ● record Person(String name, int age) {} ● 他の型を継承することも、他の型で継承することもできない ● インタフェースの実装は可能

Slide 34

Slide 34 text

データ型のルール ● 純粋関数をデータ型に拡張する ● 純粋データ型? ● 参照透過 ● 戻り値が引数だけで決まる ● 副作用なし ● 状態の変更や外部への入出力なし ● 参照透過 ● 戻り値が引数とフィールドだけで決まる ● 副作用なし ● フィールド変更以外の状態の変更や 外部への入出力なし

Slide 35

Slide 35 text

代数的データ型 ● 直積型 ● クラス ● 配列 ● レコードクラス ● 直和型 ● 継承 ● sealed

Slide 36

Slide 36 text

直積型 ● 値を組み合わせる型 ● レコードクラスは直積型を実現するために導入されたと考えれる ● 値のとりうるパターンがそれぞれの値のパターン数の積になる ● record A(boolean a1, byte a2) {} ● 2 x 256で512とおり

Slide 37

Slide 37 text

直和型 ● いずれかの型になる ● catch (IOException | InterruptedException ex) ● IOExceptionかInterruptedExcdeption ● java.nio.Buffer ● IntBufferかByteBufferか・・・

Slide 38

Slide 38 text

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 {}

Slide 39

Slide 39 text

データの処理を考える ● こんな値を合計する 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));

Slide 40

Slide 40 text

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();

Slide 41

Slide 41 text

パターンマッチ 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(); }

Slide 42

Slide 42 text

レーコードパターン ● パターンマッチでレコードの分解 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; }

Slide 43

Slide 43 text

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不要

Slide 44

Slide 44 text

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();

Slide 45

Slide 45 text

継承で実装してみる ● 継承での実装 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();

Slide 46

Slide 46 text

継承のほうがいい? ● すっきりして見える int total = cart.stream() .mapToInt(item -> item.product().type().calc(item.amount())) .sum();

Slide 47

Slide 47 text

継承で実現したときの欠点 ● 追うときにいろいろなクラスを見る必要がある ● 他のデータが関係あるときに対応できない ● 1パック170円、3パック500円とか ● 場合わけの型が2つあるときれいにかけない ● 商品種別と時間帯とか ● どちらかの型に場合わけが必要 ● 業務処理では一箇所にすべての型の処理をまとめるほうがいい ● システム境界では他の型が関係することがすくないので継承が有効 ● OracleConnectionがMySQLConnectionを気にしたりとかはない

Slide 48

Slide 48 text

書籍 ● WEB+DB PRESS vol.134 ● データ指向プログラミング