
はじめに
こんにちは、TIGの岸本卓也です。 Java25リリース記念ブログ連載 シリーズの記事です。
本稿では以下の JDK 25 の変更点の内、プレビュー以外の言語機能に関する変更点を中心に内容を見ていきます。
503: Remove the 32-bit x86 Port
JEP 449 (JDK 21) 辺りから 32-bit x86 サポートの廃止に向けた動きが進んできていました。 Linux 向けの 32-bit x86 関連ソースが残るのみとなっていましたが、32-bit x86 サポートのために発生していた足かせの排除やビルド・テストの簡素化のために JEP 503 にてソースが削除されました。
506: Scoped Values
JEP 429 (JDK 20) からプレビュー提供されていた Scoped Values が、プレビューを終えて正式リリースされました。本機能は主に ScopedValue
クラスにより提供され、次のような場合に役立ちます。
メソッドにデータを渡す方法として、メソッド引数で渡す方法があります。メソッド引数は単純で分かりやすい方法ですが、メソッド内でさらに別のメソッドを呼び出して…といった構成で実際にデータを使うのが深い階層のメソッドである場合、各メソッド呼び出しに引数を定義してデータをバケツリレーする必要があります。メソッド引数で渡す方法は、中間のメソッドではそのデータを使わないとしてもバケツリレーのために引数を定義する必要があるという点が課題です。
これを解決する方法として ThreadLocal
がよく使われます。例えば、
- Framework がコンテキスト情報を生成する
- そのコンテキスト情報のもとで Framework が Application を実行する
- Application が Framework の機能を呼び出す
- 呼び出された Framework 機能がコンテキスト情報を使う
といった構成において、コンテキスト情報を ThreadLocal
で渡す実装例は以下です。
public class ThreadLocalExample { |
C:\>java ThreadLocalExample.java |
この実装は期待通り動きますが、 ThreadLocal
の方法には以下の課題があります。
- 自由に変更できる:
ThreadLocal
にアクセスできるコードからはset
メソッドにより自由に値が変更できます。いつ、どこから変更されるかわからない変数の管理は難しく、バグを生みがちです。 - 無制限の存続期間:
ThreadLocal
にset
された値は、スレッドが存続する間またはremove
メソッドが呼ばれるまで保持されます。remove
メソッド呼び出しは忘れがちなため不必要に長期間変数が生存する可能性があり、スレッドプールを使用する場合は意図せずリークして脆弱性やメモリリークに繋がります。 - 継承が高コスト: 親スレッドのスレッドローカル変数を継承して子スレッドを作成できますが、全てのスレッドローカル変数に対する領域をを割り当てる必要があるため多くのメモリが必要となる可能性があります。
このような課題を解決するため、 ThreadLocal
よりも汎用性を減らし用途を絞った新たな API が ScopedValue
です。 ScopedValue
により、同一スレッド内または子スレッドとの間でより安全かつ効率的にデータを共有できます。
先程の Framework-Application の例を ScopedValue
に変更した実装例は以下です。
public class ScopedValueExample1 { |
C:\>java ScopedValueExample1.java |
ScopedValue
のスコープは以下のようにネストする (rebinding) こともできます。
private static final ScopedValue<String> NAME = ScopedValue.newInstance(); |
C:\>java ScopedValueExample2.java |
ThreadLocal
の場合は値を変更すると変更されたままです。
private static final ThreadLocal<String> NAME = new ThreadLocal<>(); |
C:\>java ThreadLocalExample2.java |
511: Module Import Declarations
モジュール単位でインポートできる仕組みです。これまでインポートの宣言方法は single-type-import と type-import-on-demand の2種類でした。それぞれの例は以下です。
single-type-import
import java.util.Map; |
type-import-on-demand
import java.util.*; |
新たに追加された module import によりモジュール単位でのインポートが可能になります。
import module java.base; |
同名クラスが存在する場合はどのクラスをインポートするか曖昧になります。そのような場合は single-type-import または type-import-on-demand のインポート宣言を追加 (shadowing) して曖昧さを解決します。
import module java.base; // java.util.Date が含まれる |
なお、当社が公開している Java コーディング規約 もそうですが、インポート宣言は明確さを優先して single-type-import が好まれることが多いです。しかし、 JShell 使用時やお試し実装など明確さより便利さを優先できる場合には有用になりそうです。
512: Compact Source Files and Instance Main Methods
おまじない/ボイラープレート少なく Java プログラムを書き始められる仕組みです。プレビューでは simple source files と呼ばれていましたが compact source files という名称に変更して正式リリースされました。
これまでは例えば Hello, World! のコードは以下のように実装する必要がありました。
public class HelloWorld { |
このコードには Hello, World! を出力するメインのコード以外に多くのコードが含まれているため、 Java 初学者を混乱させる可能性があります。
compact source files と instance main methods の仕組みを使うと、 Hello, World! のコードは以下にできます。
// class 定義を省略できる |
class 定義に囲まれていないフィールドやメソッドを含むソースを compact source file と呼び、それらのフィールドやメソッドをメンバーとする暗黙のクラスが定義されているように扱われます。暗黙に定義されるクラスは名前を持たないため、 new
演算子でインスタンス化することや static メソッドのメソッド参照はできません。しかし、後述の仕組みで作られるインスタンスを this
で参照できます。
暗黙定義されるクラスは次の仕様になります。
- 無名パッケージに定義されたトップレベルの final クラス
Object
クラスを継承し、インターフェース実装は無し- コンストラクタは引数無しのデフォルトコンストラクタのみ存在する
main メソッドは次の仕組みで選択されます。
- クラスに直接定義または継承で
String[]
を引数とするmain
メソッドがあればそのメソッドを選択する。 - クラスに直接定義または継承で引数無しの
main
メソッドがあればそのメソッドを選択する。
選択された main
メソッドは次の仕組みで起動されます。
- 選択されたメソッドが
static
ならそのメソッドを起動する。 - 選択されたメソッドがインスタンスメソッドなら、引数無しのコンストラクタを起動してインスタンスを作成してから選択されたメソッドを起動する。
この簡潔な実装方法は 506: Scoped Values の実装例でもしれっと使っていました。
また、 compact source files では java.base
モジュールが自動でインポートされるため、以下のようによく使うクラスもインポート宣言無しに使えます。
void main() { |
513: Flexible Constructor Bodies
コンストラクタの処理でコンストラクタ呼び出し (super(...)
や this(...)
) より前に文 (statement) を置けるようになりました。これにより、例えば以下のガード節のような実装が可能になります。
class A { |
なお、コンストラクタ呼び出しより前では this
や super
などで作成中のインスタンスの参照はできず初期化のみできます。例えば以下はコンパイルエラーとなる実装です。
class A { |
コンストラクタ呼び出しより前のインスタンスアクセスは初期化のみ可能です。
class A { |
さいごに
本稿では JDK 25 の言語機能系の変更点をピックアップして紹介しました。
今回始めて Java のリリース内容を調査しましたが、 JDK 25 のページ やここからリンクされている各 JEP のページ、Oracle 社の Updates ページ は説明やサンプルが多くとても参考になりました。