2020/04/17

Java 14 の新機能 (8) - JEP 361: Switch Expressions (Standard)

JEP 361: Switch Expressions (Standard)

switch 式が Preview から標準になりました。

switch 文と switch 式

従来から Java には switch 文があります。今回導入されたのは switch 式です。
文というのは Java プログラムの構成単位で、文を並べることで処理を記述することになっています。文は大きく分けて、単一の式からなるものと、制御構文の2種類があります。switch 文は後者の一種で、switch 式は前者に含まれます。

どちらも与えられた式の値に応じて場合分けして処理を実行するのですが、文と式では出来ることと出来ないことがそれぞれ違ってきます。switch に関して言うと、switch 式が導入されたことにより、switch 式で書いた方が良い場合が多くなってくると思いますが、switch 文でしか出来ないこともあります。

式の特徴は、評価することで特定の型の値になることが担保されていることです。switch 式もそうなります。つまり、どの場合分けに当てはまったとしても、必ず最後に同じ型の値を返さなければなりません。文は、評価しても値を持たないので、このような制約はありません。switch 文では値を返す必要はありませんし、返すことができません。
逆に switch 式で出来ないことは、単独で文を構成することです。文は単一の式から構成できますが、構成できる式の種類は限定的です。例えば 1+1 という式を 1+1; として文にすることは出来ません。switch 式も、単独で文にすることはできません。他の式の一部になっている必要があります。

switch 式の構造

switch ブロックの中で、yield 文を使って値を返すことで、switch 式になるという仕様になりました。
  private static String switchStatement(final int num) {
    String result;
    switch (num) {
    case 1:
      result = "one";
      break;
    case 2:
      result = "two";
      break;
    default:
      result = "many";
    }
    return result;
  }
  private static String switchExpression(final int num) {
    String result = switch (num) {
    case 1:
      yield "one";
    case 2:
      yield "two";
    default:
      yield "many";
    };
    return result;
  }

case xxx: yield zzz; と書く代わりに、case xxx -> zzz; という縮約記法も用意されました。':' と '->' は1つの switch 式の中に混在させることはできません。
  private static String switchExpression2(final int num) {
    String result = switch (num) {
    case 1 -> "one";
    case 2 -> "two";
    default -> "many";
    };
    return result;
  }

'->' を使った場合も、ブロックと yield を使って複文にすることができます。
  private static String switchExpression3(final int num) {
    String result = switch (num) {
    case 1 -> "one";
    case 2 -> "two";
    default -> {
      System.err.println("WARN: too many...");
      yield "many";
    }
    };
    return result;
  }

yield メソッドが呼べない?

yield の導入にあたって、少し問題が起きました。
yield はキーワード (予約語) になっていません。おそらく互換性の問題から、キーワードにしなかったのだと思います。そのため、キーワードではないが、制御構文であるという奇妙な状況になっています。
キーワードになっていないため、yield という名前のメソッドを定義できます。それを使おうと思って、yield(args); とやると、switch 式以外のところで yield を使うなと怒られてしまいます。こういうときは、this.yield(args); などとして、yield 文と解釈されないように回避しましょう。
逆に、メソッド呼び出しと間違わないようにするため、yield 文には括弧を付けない方がいいですね。 return と同じです。