Java 23新機能まとめ

Java 23が2024/9/17にリリースされました。
https://mail.openjdk.org/pipermail/jdk-dev/2024-September/009395.html
[Java 23をリリース](https://blogs.oracle.com/oracle4engineer/post/the-arrival-of-java-23-ja)
[The Arrival of Java 23](https://blogs.oracle.com/java/post/the-arrival-of-java-23)

LTSではなく新たに入った機能も少ないですが、Module Import Declarationsは長大なimport文を削減してくれそうなので期待です。

<!--
こちらの配信で解説しています。
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/1601b660-d2f8-36f7-1cba-f852930261e1.png)](https://www.youtube.com/watch?v=-5zlmBnxmCU)
-->

JDKをインストールせずに言語やライブラリの新機能を試したい場合にはJava Playgroundが便利です。
https://dev.java/playground/

Samplesに新機能のサンプルがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/de035c32-8899-64cd-66a0-546348f3c6e4.png)


## 資料

詳細はこちら
[JDK 23 Release Notes](https://jdk.java.net/23/release-notes)
[Java SE 23 Platform JSR 398](https://openjdk.org/projects/jdk/23/spec/)
[OpenJDK JDK 23 GA Release](https://jdk.java.net/23/)

APIドキュメントはこちら
[Overview (Java SE 23 & JDK 23)](https://docs.oracle.com/en/java/javase/23/docs/api/index.html)

追加されたAPIまとめはこちら
https://docs.oracle.com/en/java/javase/23/docs/api/new-list.html

APIの差分はこちら。
https://cr.openjdk.org/~iris/se/23/build/latest/java-se--jdk-22-ga--jdk-23%2B37/

<!--
Java 17以降で取り込まれたJEPはこちら
[JEPs in JDK 22 integrated since JDK 17](https://openjdk.org/projects/jdk/22/jeps-since-jdk-17)
-->

## ディストリビューション

MacやLinuxでのインストールには[SDKMAN!](https://sdkman.io/)をお勧めします

[Oracle OpenJDK](https://jdk.java.net/23/)以外に無償で商用利用できるディストリビューションとしては、次のようなものがあります。
Microsoft Buildは非LTSはお休み。

- [Oracle JDK](https://www.oracle.com/java/technologies/downloads/)
- [Adoptium Temurin](https://adoptium.net/?variant=openjdk23&jvmVariant=hotspot)
- [Liberica JDK](https://bell-sw.com/pages/downloads/#jdk-23)
- [Amazon Corretto 23](https://docs.aws.amazon.com/corretto/latest/corretto-23-ug/downloads-list.html)
- [SapMachine](https://sap.github.io/SapMachine/)
- [Azul Zulu](https://www.azul.com/downloads/?version=java-23-lts&package=jdk#zulu)
<!--
- [Microsoft Build of OpenJDK](https://learn.microsoft.com/java/openjdk/download)
-->

アップデートは10月に23.0.1が、来年1月に23.0.2がリリースされることになります。

# JEP
大きめの変更はJEPでまとまっています。
https://openjdk.org/projects/jdk/23/

今回は12個のJEPが取り込まれました。正式採用は3つです。プレビューからの正式化はありません。プレビューは9つで、そのうち2つが新たに入ったものです。
正式採用ではJavadocコメントがMarkdownで書けるようになったJEP 467が便利そう。あと、新しく入ったプレビューの２つ、switchなどでプリミティブが使えるようになるJEP 455とモジュールimportのJEP 476はどちらもコーディングでの影響が大きそうです。
また、Java 22でプレビューに入ったString Templatesは一旦取り下げられています。

[455: Primitive Types in Patterns, instanceof, and switch (Preview)](#455-primitive-types-in-patterns-instanceof-and-switch-preview)
[466: Class-File API (Second Preview)](#466-class-file-api-second-preview)
[467: Markdown Documentation Comments](#467-markdown-documentation-comments)
[469: Vector API (Eighth Incubator)](#469-vector-api-eighth-incubator)
[473: Stream Gatherers (Second Preview)](#473-stream-gatherers-second-preview)
[471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal](#471-deprecate-the-memory-access-methods-in-sunmiscunsafe-for-removal)
[474: ZGC: Generational Mode by Default](#474-zgc-generational-mode-by-default)
[476: Module Import Declarations (Preview)](#476-module-import-declarations-preview)
[477: Implicitly Declared Classes and Instance Main Methods (Third Preview)](#477-implicitly-declared-classes-and-instance-main-methods-third-preview)
[480: Structured Concurrency (Third Preview)](#480-structured-concurrency-third-preview)
[481: Scoped Values (Third Preview)](#481-scoped-values-third-preview)
[482: Flexible Constructor Bodies (Second Preview)](#482-flexible-constructor-bodies-second-preview)

# ツール

JavadocコメントでMarkdownが使えるようになりました。
[467: Markdown Documentation Comments](#467-markdown-documentation-comments)

## [467: Markdown Documentation Comments](https://openjdk.org/jeps/467)

JavadocコメントをMarkdownで書けるようになります。
その場合、スラッシュ3つでコメントを始めます。

細かいことはJEPの画像を。
![Object-hashcode-diff-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228058/879777c3-efa3-f59a-8505-d4915bcd671c.png)


# 言語機能

言語機能の変更としては、`_`をラムダパラメータやパターンマッチングなどで使えるUnnamed Variables & Patternsが正式機能になっています。また、スーパークラスのコンストラクタ呼び出しの前にステートメントを書けるStatements before superがプレビューとして導入されました。

[455: Primitive Types in Patterns, instanceof, and switch (Preview)](#455-primitive-types-in-patterns-instanceof-and-switch-preview)
[476: Module Import Declarations (Preview)](#476-module-import-declarations-preview)
[477: Implicitly Declared Classes and Instance Main Methods (Third Preview)](#477-implicitly-declared-classes-and-instance-main-methods-third-preview)
[482: Flexible Constructor Bodies (Second Preview)](#482-flexible-constructor-bodies-second-preview)

## [455: Primitive Types in Patterns, instanceof, and switch (Preview)](https://openjdk.org/jeps/455)

### instanceof

instanceofでプリミティブを使うとき、値が欠落しないかどうかが判定基準になります。

```
jshell> int i = 128
i ==> 128

jshell> i instanceof byte
$2 ==> false

jshell> i = 127
i ==> 127

jshell> i instanceof byte
$4 ==> true
```

doubleでも精度落ちするかどうかが判定されます。

```
jshell> double d = 1
d ==> 1.0

jshell> d instanceof int
$2 ==> true

jshell> d = 1.2
d ==> 1.2

jshell> d instanceof int
$4 ==> false
```

ただし、ラッパークラスのオブジェクトの場合はその基本型のときだけtrueになります。

```
jshell> Long num = 123L
num ==> 123

jshell> num instanceof int
|  エラー:
|  不適合な型: java.lang.Longをintに変換できません:
|  num instanceof int
|  ^-^

jshell> num instanceof long
$7 ==> true
```

そしてパターンマッチとして使えるわけですね。

```
jshell> long num = 123
num ==> 123

jshell> var out = new ByteArrayOutputStream()
out ==>

jshell> if (num instanceof byte b) out.write(b)

jshell> out.toByteArray()
$21 ==> byte[1] { 123 }

jshell> num = 1234
num ==> 1234

jshell> if (num instanceof byte b) out.write(b)

jshell> out.toByteArray()
$24 ==> byte[1] { 123 }
```

### switch

このパターンマッチがswitchでも使えるということなんですけど、定数パターンもあるので、switchですべての型が使えるようになったということになります。

```
jshell> long num = 1234
num ==> 1234

jshell> switch (num) { case int n -> "いんと"; default -> "それ以外";}
$25 ==> "いんと"

jshell> num = 30_0000_0000L
num ==> 3000000000

jshell> switch (num) { case int n -> "いんと"; default -> "それ以外";}
$27 ==> "それ以外"
```

if式のようなこともできますね。
```
boolean flag = isFoo();
var s = switch (flag) {
  case true -> "たった";
  case false -> "おりた";
}
```

## [476: Module Import Declarations (Preview)](https://openjdk.org/jeps/476)

モジュールごとのimportができるようになりました。java.baseをimportしたらだいたいいけるってなりそうです。あと、外部ライブラリのモジュール対応が進みそうです。

次のようにすると、java.baseモジュールに属するjava.utilもjava.ioもimportされることになります。

```java
import module java.base;
```

JShellでも`--enable-preview`をつけるとデフォルトでjava.baseがimportされるようになります。
例えば、java.timeはimportされていなかったのでLocalDateをそのまま使うとエラーになります。

```
>jshell
|  JShellへようこそ -- バージョン23
|  概要については、次を入力してください: /help intro

jshell> LocalDate.now()
|  エラー:
|  シンボルを見つけられません
|    シンボル:   変数 LocalDate
|    場所: クラス
|  LocalDate.now()
|  ^-------^
```
`--enable-preview`を付けるとimport不要で使えます。

```
>jshell --enable-preview
|  JShellへようこそ -- バージョン23
|  概要については、次を入力してください: /help intro

jshell> LocalDate.now()
$1 ==> 2024-10-11
```

SwingでGUIプログラムをする場合には`java.desktop`モジュールをimportします。

```
jshell> import module java.desktop;

jshell> var f = new JFrame("hello")
f ==> javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden, ... tPaneCheckingEnabled=true]

jshell> var b = new JButton("OK")
b ==> javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0 ... xt=OK,defaultCapable=true]
```

ただ、そうすると`List`が`java.util.List`と`java.awt.List`でかぶるので、あいまいになってそのままで使えなくなります。

```
jshell> List a = null;
|  エラー:
|  Listの参照はあいまいです
|    java.awtのクラス java.awt.Listとjava.utilのインタフェース java.util.Listの両方が一致します
|  List a = null;
|  ^--^
```

この場合には`java.util.List`など実際に使うほうをimportするなどが必要です。

```
jshell> import java.util.List

jshell> List a = null
a ==> null
```

## [477: Implicitly Declared Classes and Instance Main Methods (Third Preview)](https://openjdk.org/jeps/477)

3rd Previewになりました。`java.base`モジュールがimportされることと、java.io.IOが導入されたこと、そしてそのメソッドがimportされてprintlnだけで使えるようになったことがJava 22との違いです。

ﾊﾟﾌﾞﾘｯｸｽﾀﾃｨｯｸｳﾞｫｲﾄﾞﾒｲﾝの呪文から解放されるやつです。
[Javaがﾊﾟﾌﾞﾘｯｸｽﾀﾃｨｯｸｳﾞｫｲﾄﾞﾒｲﾝの呪文から解放される - きしだのHatena](https://nowokay.hatenablog.com/entry/2023/06/12/153755)

Javaでは単純なハローワールドを書くために次のようなコードが必要でした。
```java
public class Hello {
  public static void main(String[] args) {
    System.out.println("Hello Java!");
  }
}
```

これが次のように書けるようになります。
```java
void main() {
  println("Hello Java!");
}
```

プレビュー機能なので、`--enable-preview`が必要です。`javac`では`--source 23`も必要になります。ソースを直接実行する場合は`java`コマンドでも`--source 23`が必要です。
```
>more Hello.java
void main() {
  println("Hello Java!");
}

>java --enable-preview --source 23 Hello.java
ノート: Hello.javaはJava SE 23のプレビュー機能を使用します。
ノート: 詳細は、-Xlint:previewオプションを指定して再コンパイルしてください。
Hello Java!
```

`public`や`class`、`static`などのキーワードが消え、`[]`という謎の記号も消えました。
プログラムを勉強するときに、まずやりたいことは処理を書くことです。
クラスは書いた処理をうまく構成するための仕組みなので、処理が書けないうちに勉強してもあまり意味がありません。
`public`などアクセス指定はプログラムが大きくなったときに不適切な要素を使ってしまわないための仕組みなので、入門時のサンプルでは不要です。
`static`を説明するにはクラスやインスタンスの理解が必要になりますが、処理が書ける前に勉強するには早すぎます。
配列も変数を知らないうちに勉強できるものでもなく、入門時のサンプルで引数`args`を使うことはあまりありません。
その結果「よくわからないしきたり」のまま放置されがち、というか放置せざるを得ない状態で「System.out.pritlnというのは～」という説明をすることになりますが、クラス名とファイル名が違うので動かせなくてハマってそこまでたどりつけなかったりもします。

ということで、初期に学習するべきことに集中できるようにするために、次のように制約が緩和されました。

- クラスの定義が不要になる
- mainメソッドはインスタンスメソッドでよくなる
- mainメソッドの引数を省略できる
- mainメソッドがpublicじゃなくてもよくなる

「メソッドも不要でいいのでは？」となると思いますが、現状ではステートメントとメソッドを同レベルで書く仕組みがないため、新たにローカルメソッドのような仕組みが必要になり、「初期に学習するべきことに集中できるようにするため」としては影響範囲が大きいので残されています。

このあたりは、次のデザインノートにまとめられています。
https://openjdk.org/projects/amber/design-notes/on-ramp

### mainメソッドはインスタンスメソッドでもよくなる

mainメソッドにstaticをつけなくてもよくなります。そして、mainメソッドにstaticをつけなくてもいいということは、そこから呼び出すメソッドなどにもstaticをつけなくていいということになるので、少し大きめのサンプルが書きやすくもなります。

```java
public class Hello {
  public void main(String[] args) {
    foo();
  }
  void foo() {
    System.out.println("Hello");
  }
}
```

### mainメソッドの引数を省略できる / mainメソッドがpublicじゃなくてもよくなる

書かなくてよさそうなものを書かずにすむのですっきりします。
mainメソッドをprivateにすることはできません。protectedは可能です。
「public static void main(String[] args)」を何も見ずに書けるようになったときにJavaに馴染んだ満足感があったので、それがなくなるのは寂しいですが、単なるノスタルジーなのでなくていいと思います。

### クラスの定義が不要

クラスを知らなくていいことの他に、クラス名を考えなくていいとかインデントが一段浅くなるとか、中カッコが一組だけになるので間違いが減るとか、いろいろ入門がやりやすくなります。
クラスを省略してmainメソッドにインスタンスメソッドを使うと、`new Object(){}`で囲まれることになりました。
```java
new Object() {
  void main() {
    println("Hello Java");
  }
}.main();
```

ただ、クラス定義を省略する場合、ファイル名にstatic.javaなどキーワードを付けるとエラーになります。クラス定義がある場合、`java`コマンドで直接実行であれば問題ないです。

```
>java --enable-preview --source 23 static.java
static.java:3: エラー: 不正なファイル名: static
  void main() {
  ^
エラー1個
エラー: コンパイルが失敗しました
```

### java.io.IOがstatic importされる

[`java.io.IO`](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/io/IO.html)というクラスが導入されました。このクラスには、`print`、`println`、`readln`の3つのstaticメソッドが定義されています。
クラス定義を省略して暗黙のクラスを使う場合、これらのメソッドがstaticインポートされるので、`System.out`を書く必要がなくなります。

ただし、クラスを定義する場合には自動インポートは行われないので、`System.out`を書くか明示的にインポートが必要です。
先ほどの例で`System.out`を書いていましたが、`java.io.IO`をstaticインポートすることで省略できます。

```java
import static java.io.IO.*;

public class Hello {
  public void main(String[] args) {
    foo();
  }
  void foo() {
    println("Hello");
  }
}
```

### java.baseがmodule importされる

暗黙のクラスを使う場合、java.baseモジュールもimportされます。
こういうコードはimportなしで書けるようになります。

```java
void main() {
 var data = List.of("test", "data");
 println(data.stream().collect(Collectors.joining(" ")));
}
```

## [482: Flexible Constructor Bodies (Second Preview)](https://openjdk.org/jeps/482)

States before superだったものが名前が変わって2ndプレビューになりました。

スーパークラスのコンストラクタ呼び出しの前に`this`を使わないステートメントが書けるようになります。

例えばListを2つコンストラクタにとるクラスがあるとします。

```java
class Foo {
  Foo(List l1, List l2) {
  }
}
```

このコンストラクタを継承するとき、Listをふたつ渡す必要があります。 
```java
class Bar extends Foo {
  Bar() {
    super(List.of("abc", "def"), List.of("abc", "def"));
  }
}
```

同じものを渡しているので共通化したいですが、`super`の前でステートメントを実行できないので迂回を考える必要があります。

```java
class Bar extends Foo {
  private Bar(List l) {
    super(l, l);
  }
  Bar() {
    this(List.of("abc", "def"));
  }
}
```

これを、次のように書けるようになります。

```java
class Bar extends Foo {
  Bar() {
    var param = List.of("abc", "def");
    super(param, param);
  }
```

このように、パラメータの検証や構築、共有がある場合に、自然なコードで書けるようになります。
次のようなフィールドのアクセスを`super`の前に行うとエラーになります。

```java
class Bar extends Foo {
  List<String> param;
  Bar() {
    param = List.of("abc", "def");
    super(param, param);
  }
}
```

```
test.java:11: エラー: スーパータイプのコンストラクタの呼出し前はparamを参照できません
    param = List.of("abc", "def");
    ^
```

# API

APIの変更としては6つのJEPがありますが新しく入ったのはsun.misc.UnsafeでのメモリアクセスAPIが非推奨になったものだけです。JEPになっていない変更が少しあります。

[466: Class-File API (Second Preview)](#466-class-file-api-second-preview)
[469: Vector API (Eighth Incubator)](#469-vector-api-eighth-incubator)
[473: Stream Gatherers (Second Preview)](#473-stream-gatherers-second-preview)
[471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal](#471-deprecate-the-memory-access-methods-in-sunmiscunsafe-for-removal)
[480: Structured Concurrency (Third Preview)](#480-structured-concurrency-third-preview)
[481: Scoped Values (Third Preview)](#481-scoped-values-third-preview)

## 小さいもの

JEPになっていないAPI変更で、動きがわかりやすいものを挙げます。

### [NumberFormat.isStrict/setStrict](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/text/NumberFormat.html#isStrict())

NumberFormatのparseでは数値と関係ない文字が続いていてもパースしてくれていましたが、setStrict(true)すると例外になります。

```
jshell> var f = NumberFormat.getInstance()
f ==> DecimalFormat [locale: "日本語 (日本)", pattern: "#,##0.###"]

jshell> f.isStrict()
$9 ==> false

jshell> f.parse("1,234円")
$10 ==> 1234

jshell> f.setStrict(true)

jshell> f.parse("1,234円")
|  例外java.text.ParseException: Unparseable number: "1,234円"
|        at NumberFormat.parse (NumberFormat.java:464)
|        at (#12:1)
```

### [Instant.until(Instant)](https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/time/Instant.html#until(java.time.Instant))
https://bugs.openjdk.org/browse/JDK-8331202

Instantに、他のInstantとの差をDurationで返すメソッドが入りました。
`Duration(startInstant, endInstant)`と同じです。

```
jshell> var i = Instant.now()
i ==> 2024-10-08T21:08:15.820407600Z

jshell> i.until(Instant.now())
$19 ==> PT24.3460241S

jshell> i.until(Instant.now())
$20 ==> PT30.088317S
```

## [466: Class-File API (Second Preview)](https://openjdk.org/jeps/466)

Javaクラスファイルを解析、変更、生成するためのAPIです。2ndプレビューになりました。
ASMやJavassistなど、同様の機能を実装するライブラリがありますが、Javaではjavacという標準のクラスファイル生成ツールがあります。また、内部的にASMを使っている部分もあります。
ただ、ASMのように外部ライブラリを使うと、こういった外部ライブラリの新機能対応は新バージョンがリリースされるまで正式化しません。そうすると、Javaの新機能をJava内部で使いたいとき、そのASMは非公式であるかひとつ前のバージョン対応であるかということになってしまいます。
そこで、Java標準としてクラスファイル操作のAPIを提供するということになりました。

## [469: Vector API (Eighth Incubator)](https://openjdk.org/jeps/469)

AVX命令のような、複数のデータに対する計算を同時に行う命令をJavaから利用できるようになります。
使うためには実行時やコンパイル時に`--add-modules jdk.incubator.vector`をつける必要があります。

Java 16でインキュベータとして導入されたPanamaプロジェクトの残る片割れですが、Java 22からの変更なく8th Incubatorになりました。Project Valhallaのvalue classを使いたいようで、関連JEPがpreviewになるまではIncubatorのままということです。

`MemorySegment`にはバイト配列に対してVectorアクセスができていましたが、プリミティブ配列にVectorアクセスできるようになったようです。

基本的な使い方は次のようになります。

```java
import jdk.incubator.vector.*;
static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;

void vectorComputation(float[] a, float[] b, float[] c) {

    for (int i = 0; i < a.length; i += SPECIES.length()) { // SPECIES.length() = 256bit / 32bit -> 8
        VectorMask<Float> m = SPECIES.indexInRange(i, a.length); // 端数がマスクされる
                                                                 // a.lengthが11でiが8のとき最初の3つしか要素がないので [TTT.....]
		// FloatVector va, vb, vc;
        FloatVector va = FloatVector.fromArray(SPECIES, a, i, m);
        FloatVector vb = FloatVector.fromArray(SPECIES, b, i, m);
        FloatVector vc = va.mul(va).
                    add(vb.mul(vb)).
                    neg();
        vc.intoArray(c, i, m);
    }
}
```

利用できるのは次の6つの型です。それぞれに対応するVector型があって、これが基本になります。

|型|bit幅|Vector|
|--|--|--|
|byte|8|ByteVector|
|short|16|ShortVector|
|int|32|IntVector|
|long|64|LongVector|
|float|32|FloatVector|
|double|64|DoubleVector|

ただ、利用するにはVectorSpeciesが必要です。利用したいVectorにSPECIES_\*という定数が用意されているので、それを使います。\*は一度に計算するbit数ですね。

```
jshell> FloatVector.SP
SPECIES_128         SPECIES_256         SPECIES_512      
SPECIES_64          SPECIES_MAX         SPECIES_PREFERRED   
```

MAXではそのハードウェアで使える最大、PREFERREDは推奨ビット数だけど、同じになるんじゃないのかな。ここでは256bitが推奨されて、floatが8個同時に計算できるようになっていますね。

```
jshell> FloatVector.SPECIES_PREFERRED
$11 ==> Species[float, 8, S_256_BIT]
```

ハードウェアで使えるbit数は搭載CPUに依存しますが、普通のIntel/AMDであれば256、XEONとか つよつよCPUなら512かな。M1は128でした。ハードウェアでサポートされないbit数を使おうとするとソフトウェア処理になるので遅くなります。

実際のVectorはfrom*というメソッドで取得します。fromArray、fromByteArray、fromByteBufferが用意されています。インキュベータに入る前はfromValuesがあったのですが、なくなってますね。

Vectorを得られたら、用意されたメソッドで計算します。ひととおりの算術命令はあります。

```
jshell> va.
abs()                    add(                     addIndex(
bitSize()                blend(                   broadcast(
byteSize()               castShape(               check(
compare(                 compress(                convert(
convertShape(            div(                     elementSize()
elementType()            eq(                      equals(
expand(                  fma(                     getClass()
hashCode()               intoArray(               intoMemorySegment(
lane(                    lanewise(                length()
lt(                      maskAll(                 max(
min(                     mul(                     neg()
notify()                 notifyAll()              pow(
rearrange(               reduceLanes(             reduceLanesToLong(
reinterpretAsBytes()     reinterpretAsDoubles()   reinterpretAsFloats()
reinterpretAsInts()      reinterpretAsLongs()     reinterpretAsShorts()
reinterpretShape(        selectFrom(              shape()
slice(                   species()                sqrt()
sub(                     test(                    toArray()
toDoubleArray()          toIntArray()             toLongArray()
toShuffle()              toString()               unslice(
viewAsFloatingLanes()    viewAsIntegralLanes()    wait(
withLane(  
```

ところで、こういったメソッド呼び出しの内部でAVX命令などを呼び出すのでは遅くなるんではという気がしますが、実際にはJVM intrinsicsという仕組みでJITコンパイラがこれらのメソッド呼び出しをネイティブ関数呼び出しに置き換えます。


## [473: Stream Gatherers (Second Preview)](https://openjdk.org/jeps/473)

Java 22でプレビューとして導入され、評価期間を確保するためそのまま2ndプレビューになりました。

Streamでは、前の値を参照するような処理や、処理順序を前提とした処理が行えません。そのため、前の値にどんどん手を加えてリストを作ったり、移動平均をとったりといったことができませんでした。
そういった、順番を保証して前の値を踏まえた処理が行えるようにするのが、Gathererです。

デザインノートはこちら
https://cr.openjdk.org/~vklang/Gatherers.html

Collectorと同じで自前のGathererを実装するのは大変そうですが、CollectorsのようにGatherersが用意されています。ここでは、Gatherersだけ紹介します。

Streamが使えそうで使えなかった処理に、Streamが使える場合が増えそうです。

JShellを使っていますが、見やすいように改行しているので、そのとおりに入力はできません。試すときは改行せず一行で入力してください。

### windowFixed

要素を指定した個数分まとめてListにします。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.windowFixed(3))
                 .toList()
$1 ==> [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
```

### windowSliding

スライディングウィンドウです。指定した要素分を、要素をずらしながらとっていってListをつくります。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.windowSliding(3))
                 .toList()
$2 ==> [[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5],
        [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9]]
```

### mapConcurrent

指定した個数分、並列に処理を行いながらmapします。このとき並列処理にはVirtualThreadが使われます。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.mapConcurrent(4, n -> n * 2))
                 .toList()
$3 ==> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
```

処理結果としてはmapと同じになります。

```
jshell> IntStream.range(0, 10).boxed()
                 .map(n -> n * 2)
                 .toList()
$4 ==> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
```

### scan

計算を追加していきます。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.scan(() -> "*", (s, n) -> s + n))
                 .toList()
$7 ==> [*0, *01, *012, *0123, *01234, *012345,
        *0123456, *01234567, *012345678, *0123456789]

```

### fold

処理順を前提としたreduce処理です。

```
jshell> IntStream.range(0, 10).boxed()
                 .gather(Gatherers.fold(() -> "*", (s, n) -> s + n))
                 .toList()
$8 ==> [*0123456789]
```

reduceではBinaryOperatorをとるので、要素と結果の型が同じになる必要があります。foldはBiFunctionをとるので、値と結果の型が同じである必要はありません。

```
jshell> IntStream.range(0, 10).boxed().reduce(0, (a, b) -> a + b)
$9 ==> 45
```

Collectors.reducingと違うのは、parallel streamでも処理順が保証されることです。

```
jshell> IntStream.range(0, 10).parallel().boxed()
                 .collect(Collectors.reducing(
                         "*", n -> "" + n, (s, t) -> s + t))
$12 ==> "*0*1*2*3*4*5*6*7*8*9"
```

あと、終端処理が必要になりますね。

```
jshell> IntStream.range(0, 10).parallel().boxed().gather(Gatherers.fold(() -> "*", (s, n) -> s + n)).findAny().get()
$14 ==> "*0123456789"
```

## [471: Deprecate the Memory-Access Methods in sun.misc.Unsafe for Removal](https://openjdk.org/jeps/471)

Foreign memory APIが正式化されて代替手段が整ったことで、`sun.misc.Unsafe`でのメモリアクセスAPIが非推奨になりました。
次のLTSであるJava 25までに実行時警告が出るようになり、Java 26以降で例外が発生するようになり、その後で削除されるという流れが予定されています。

## [480: Structured Concurrency (Third Preview)](https://openjdk.org/jeps/480)

Java 19でIncubatorとして含まれていましたが、Java 21、22から変更なく3rd Previewになりました。

並列処理では、複数の処理を実行するときに、両方が終われば正常終了とか、どちらか片方が終われば終了だとか、どちらか一方でも例外が発生したら終了だとか、同時に行う処理で連動することがあります。
しかし、これを既存の`join`や`wait`などで制御しようとすると、実際には`join`から`wait`へのGo Toを書くようなコードになって、処理が追えなくなります。
そこで導入されるのが構造化並列性といいます。
こんな感じ。詳しくはあとで書きます！(Java 19のときから言ってる・・・)

```java
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Subtask<String>  user  = scope.fork(() -> findUser());
        Subtask<Integer> order = scope.fork(() -> fetchOrder());

        scope.join()            // Join both forks
             .throwIfFailed();  // ... and propagate errors

        // Here, both forks have succeeded, so compose their results
        return new Response(user.get(), order.get());
    }
}
```

`Subtask`は`Supplier`を継承しているので、`Supplier`として扱うほうがいいかもしれません。

## [481: Scoped Values (Third Preview)](https://openjdk.org/jeps/481)

Java 20でIncubateになりJava 21でプレビュー、そのままJava 22で2ndプレビューになりましたが、少し変更が加わって3rd Previewになりました。

同じスレッド内で値を共有したいとき`ThreadLocal`を使いますが、値の変更が可能であったり子スレッドに値が引き継がれたり少し重いので、より限定された仕組みを提供する、ということのようです。
つまり、値を引数でひっぱりまわすのは面倒なのでグローバル変数的にstaticフィールドを使いたい程度のモチベーションで値を共有化するときに、スレッドセーフのためのThreadLocalは重すぎる、という感じですね。
たとえば次のような処理があります。

```java
void start() {
  proc1("test");
}

void proc1(String str) {
  System.out.println(str);
  proc2(str);
}

void proc2(String str) {
  System.out.println(str);
}
```

これを、全部のメソッドにいちいち引数を設定して値をひきまわるのは面倒なのでフィールドを使おう、という場合。

```java
String str;

void start() {
  str = "test";
  proc1();
}

void proc1() {
  System.out.println(str);
  proc2();
}

void proc2() {
  System.out.println(str);
}
```

これは複数スレッドから呼び出されると正しく動かないことがあります。
スレッドセーフにするために`ThreadLocal`を使っていました。

```java
final ThreadLocal<String> VAR = new ThreadLocal<>();

void start() {
  VAR.set("test");
  proc1();
}

void proc1() {
  System.out.println(VAR.get());
  proc2();
}

void proc2() {
  System.out.println(VAR.get());
}
```

しかし、引数を書いて値を持ちまわっていくのめんどいね、くらいのモチベーションで使うには`ThreadLocal`は重過ぎるので、軽量な値共有手段として`ScopedValue`が導入されます。

```java
final ScopedValue<String> VAR = new ScopedValue<>();

void start() {
  ScopedValue.where(VAR, "test")
    .run(() -> proc1());
}

void proc1() {
  System.out.println(VAR.get());
  proc2();
}

void proc2() {
  System.out.println(VAR.get());
}
```

# JVM

JVMの変更として1つJEPが導入されています。

[474: ZGC: Generational Mode by Default](#474-zgc-generational-mode-by-default)

## [474: ZGC: Generational Mode by Default](https://openjdk.org/jeps/474)

Java 21で[JEP 439](https://openjdk.org/jeps/439)として入ったZGCでの世代別GCがデフォルトになりました。
旧来の非世代別アルゴリズムはそのうち削除の方向です。

# JDK

サポートプラットフォームなどJDKの変更はありません。