Java21で追加される無名パターン(JEP 443 Unnamed Patterns and Variables)

blog1.mammb.com


はじめに

Java21あたりで入るかもしれない Unnamed Patterns and Variables(JEP 443) の先取りです。

Unnamed Patterns and Variables では、他の言語で良くある通り、アンダースコア _ で無名パターンや無名変数を記述できるようになります。

無名パターン/無名変数により以下が期待できます。

  • レコードパターンの可読性向上
  • 未使用変数の特定による保守性向上


Unnamed variables

次のコードでは、Order をイテレートしますが Order 自体は利用しません。

このようなケースで、未使用の変数に Unnamed variables _ を使用できます。

int acc = 0;
for (Order _ : orders) {
    if (acc < LIMIT) { 
        acc++;
    }

以下のようなケースでも _ を使用できます。

for (int i = 0, _ = sideEffect(); i < 10; i++) {
    ...
}

次のコードはデータをデキューしますが、3 つの要素のうち 1 つしか必要としません。

未使用の変数として _ を複数回使用できます。

while (q.size() >= 3) {
    var x = q.remove();
    var _ = q.remove();
    var _ = q.remove(); 
    var p = new Point(x, 0);
}

例外を処理するが、例外自体は不要な場合は以下のように書くことができます。

String s = ...;
try { 
    int i = Integer.parseInt(s);
    ...
} catch (NumberFormatException _) { 
    System.out.println("Bad number: " + s);
}

try-with-resources でリソースがコンテキストを表すため、コードではコンテキストを直接使用しない以下のようなケースでも _ を使用できます。

スコープを抜ければリソースは開放されます。

try (var _ = ScopedContext.acquire()) {
    // no use of acquired resource
}

ラムダパラメータでも _ を使用できます。

以下の例では、キーに文字列ストリームの内容、値に固定値を設定したマップを生成する例です。

stream.collect(Collectors.toMap(
    String::toUpperCase, _ -> "NODATA"));


Unused patterns

Unused patterns は、型と変数を合わせて _ としてパターン記述します。

instanceof のレコードパターンで以下のように書くことができます。

record Point(int x, int y) { }

Object obj = new Point(1, 2);
if (obj instanceof Point(int x, _)) {
    System.out.println(x);
}

switch で以下のように書くことができます。

switch (obj) {
    case Point(int x, _) -> //...
    ...
}

以下のようなネストしたケースにおいても Unused patterns を利用できます。

record Point(int x, int y) { }
enum Color { RED, GREEN, BLUE }
record ColoredPoint(Point p, Color c) { }

if (r instanceof ColoredPoint(Point(int x, _), _)) {
   // use x
}


Unnamed pattern variables

Unnamed pattern variables は、型定義は記載し、変数名を _ 定義します。

if (obj instanceof Point(int x, int _)) { }

前述の例は以下のように書いても同じです。

if (r instanceof ColoredPoint(Point(int x, int _), Color _)) {
   // use x
}

Unnamed pattern variables は、複数のケースで同じアクションをするケースで便利です。

sealed abstract class Ball permits RedBall, BlueBall, GreenBall { }
final  class RedBall   extends Ball { }
final  class BlueBall  extends Ball { }
final  class GreenBall extends Ball { }

record Box<T extends Ball>(T content) { }

switch (b) {
    case Box(RedBall _), Box(BlueBall _) -> processBox(b);
    case Box(GreenBall _)                -> stopProcessing();
    case Box(_)                          -> pickAnotherBox();
}

case 句の最初の2つのケースでは Unnamed pattern variables を使用しています。 3番目のケースでは、Unused patterns により、残りの全てのケースにマッチさせています。


まとめ

JEP 443 としてプレビュー提案となった Unnamed Patterns and Variables を紹介しました。

_ による無名変数は、古くより検討がありましたが、 record patterns に合わせて導入の機運が高まっています。 タイミング的に、LTS となる Java21 には入らないかもしれませんが、さっさと入ってほしいものです。