フューチャー技術ブログ

Java 23 リリース記念連載 | 第2回 JDK 22 新機能紹介

はじめに

コアテクノロジーグループの前川です。

JDK 23のリリースを受けてバージョン21~23にかけての変更点を紹介する連載企画の2本目の記事です。

JDK 22でのアップデート内容から正式採用となった以下についてピックアップしてご紹介します。

これ以外の変更点についてはOpenJDKのJDK 22のページOracle JDK Migration Guideを参照してください。

JEP 456: Unnamed Variables & Patterns (無名変数と無名パターン)

無名変数(Unnamed Variables)と無名パターン(Unnamed Patterns)は、使用されない変数にフォーカスした新機能です。アンダースコア _ で表現されます。

無名変数

無名変数は、使わない変数を示すために使用されます。

下記の例では、配列や Optional オブジェクトの各要素を使用しないことを _ で明示しています。

for (String _ : new String[]{"A", "B", "C"}) {
// すべての要素もインデックスも無視して”要素の個数分だけ処理を繰り返す”事だけにフォーカスする
}

anyOpt.map(_ -> "foo").orElseGet(() -> "bar"); // anyOpt の内容”の有無”にのみ依存した値を返す

無名パターン変数

無名パターン変数は、型検査を行う際に特定の型の変数を参照しない場合に使用されます。下記の例では、 Integer 型のオブジェクトが無視されます。

switch (obj) {
case String s -> System.out.println("String: " + s);
case Integer _ -> System.out.println("Ignored integer");
default -> System.out.println("Other type");
}

無名パターン

パターンマッチングの一部で型も省略してワイルドカード的に使用する事も可能です。

class PointNumber<P extends Number> {
final P x;
final P y;
PointNumber(P x, P y) {
this.x = x;
this.y = y;
}
public String toString() { return x + "," + y; }
}

class PointInteger extends PointNumber<Integer>{
PointInteger(Integer x, Integer y) {
super(x, y);
}
}

class PointDouble extends PointNumber<Double>{
PointDouble(Double x, Double y) {
super(x, y);
}
}

record Line(PointNumber<?> start, PointNumber<?> end) {
}

public class App {
public static void main(String[] args) throws Exception {
var line = new Line(new PointInteger(1, 2), new PointDouble(30.0, 40.0));
System.out.println(line);

switch (line) {
case Line(_, PointInteger end) -> System.out.println(end);
case Line(PointInteger start, _) -> System.out.println(start); // 1,2 が出力される
case Line(_, _) -> System.out.println("Unknown type");
}
}
}

継承/実装したメソッドの未使用引数の明示…には使えない

「基底クラスのメソッドでは受け取る事になっている引数を、このサブクラスでは使用しないよ」と保証する用途に使えそう、と思いきやそうした使用方法はサポートされていません。

public class A implements B{
public void method(int _) { // コンパイルエラー
}
}

interface B {
void method(int x);
}
As of release 22, '_' is only allowed to declare unnamed patterns, local variables, exception parameters or lambda parameters

JEP 458: Launch Multi-File Source-Code Programs (複数ファイルのソースコードからの起動)

Java 11 での機能追加「JEP 330: 単一ファイルのソースコードからの起動」により、main メソッドを持つクラスの単独のソースコードファイルをコンパイルすること無く java コマンドで実行できるようになりました。

Main.java
public class HelloWorld { // JEP 330/458により起動されるソースファイルの場合、publicクラス名とファイル名が一致している必要は無い
public static void main(String[] args) {
System.out.println(HelloWorld.class.getName());
}
}
$ java Main.java
HelloWorld

JEP 458 では java コマンドの引数に指定されたソースファイルから参照されているソースファイルも順次コンパイルされて実行できるようになりました。

Main.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println(Clazz.class.getName());
}
}
Clazz.java
public class Clazz {
}

$ java Main.java
Clazz

JEP 330 からの注意すべき変更として一点、java コマンド実行時のソースファイルまでのパス(の終端)と package 文とが整合している事が要求されるようになりました。

src/Main.java
package main;
$ java src/Main.java
エラー: ソース・ファイルへのパスの終わりがパッケージ名mainと一致しません: src/Main.java

JEP 454: Foreign Function & Memory API (外部機能 & メモリ API)

JNIに代わりJVM外のライブラリの関数をJavaから呼び出すことができるAPIと、ヒープ外のメモリにアクセスするAPIのセットです。 java.lang.foreign パッケージを使用します。

import java.lang.foreign.Arena;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.Linker;
import java.lang.foreign.SymbolLookup;
import java.lang.invoke.MethodHandle;

public class FFM {
public static void main(String[] args) throws Throwable {
Linker linker = Linker.nativeLinker();

// アリーナ内でメモリを確保する。Arena#close の呼出しによりメモリは解放される。
try (Arena arena = Arena.ofConfined()) {
// OS依存でライブラリのルックアップ方法が変わり、Windowsでは anyLibrary.dll というDLLファイルは anyLibrary という名前でもロード可能。
SymbolLookup lib = SymbolLookup.libraryLookup("libhello_ffm.so", arena);
// 戻り値 void, 引数無しの hello 関数を取得する
MethodHandle hello = linker.downcallHandle(
lib.find("hello").orElseThrow(),
FunctionDescriptor.ofVoid());
// 実行
hello.invokeExact();
}
}
}

Java側のコーディング方法以外でのJNIからの大きな変更点として以下の作業が不要になりました。

  • javac -h でJNI用のヘッダファイルを作成
  • 呼び出されるライブラリ側の実装で JNIEXPORT などを使用してJNI向けに関数を公開

おわりに

Preview や Incubator の変更点も含めると他にも色々ありますが、ここで取り上げた物を振り返ると「普段はあまり多用しないものの、いざ必要となった時の手順が煩雑であった点」が改善され、シンプルに使えるようになったように思います。

次回は武田さんのJDK 23のご紹介になります。