2020/04/10

Java 14 の新機能 (1) - JEP 305: Pattern Matching for instanceof (Preview)

JEP 305: Pattern Matching for instanceof (Preview)

instanceof 演算子にパターンマッチング機能が追加されました。いわゆる instanceof-and-cast idiom を書かずに済むようにするための文法拡張です。Preview なので、明示的に Preview レベルの機能を有効にしてあげないと使えません。

instanceof-and-cast idiom とは、例えばデータクラスの equals() を定義するときに、次のように書くことありますよね。
public class Point {
  private final int x;
  private final int y;
  public Point(final int x, final int y) {
    this.x = x;
    this.y = y;
  }
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Point) {
      Point p = (Point) obj;
      return this.x == p.x && this.y == p.y;
    }
    return false;
  }
}

instanceof で型を調べ、大丈夫ならキャスト。equals() に限って言えば IDE や lombok で生成することが多くなりましたから、今どき手書きすることもないでしょうが、このパターンは何度となく書いてきましたよね。

パターンマッチングが導入されて、こう書けるようになりました。
  @Override
  public boolean equals(Object obj) {
    if (obj instanceof Point p) {
      return this.x == p.x && this.y == p.y;
    }
    return false;
  }

三項演算子でも機能しますので、こういう書き方も可能です。
  @Override
  public boolean equals(Object obj) {
    return obj instanceof Point p ? this.x == p.x && this.y == p.y : false;
  }

ショートサーキット演算子でも機能しますので、こうも書けます。
  @Override
  public boolean equals(Object obj) {
    return obj instanceof Point p && this.x == p.x && this.y == p.y;
  }

instanceof-and-cast idiom では、instanceof で調べる型とその後キャストする型が一致していなくてもコンパイルエラーにはならないので、静的検証ツール (FindBugs/SpotBugs など) でチェックしていましたが、そういう心配は無くなります。
おそらく今後は、パターンマッチングになっていない instanceof 演算子の使用そのものを潜在的リスクとして指摘することになると思います。

バインド変数のスコープは、式やステートメントのセマンティクスが考慮されて決まります。つまり、評価値が真のときのみ実行されるということが式やステートメントの構造から明確なブロックやオペランドで有効となるようになっています。例えば以下のようになります。
  1. if (obj instanceof Point p) { p が使える } else { p は使えない }
  2. if (!(obj instanceof Point p)) { p は使えない } else { p が使える }
  3. obj instanceof Point p ? p が使える : p は使えない
  4. if (obj instanceof Point p || obj == null) { p は使えない } else { p は使えない }
    • obj == null という条件が付いたため、どちらのブロックも不確実になった
  5. if (obj instanceof Point p || false) { p は使えない } else { p は使えない }
    • || false が付いても真偽値は変わらないが、構造だけで評価するので、どちらのブロックも不確実となってしまう
  6. if (obj instanceof Point p && p が使える) { p が使える } else { p は使えない }
    • ショートサーキット演算子は正しく評価される
合理的で現実的な書き方としては、1, 3, 6 が出来れば十分でしょう。2, 4, 5 みたいなことをしたがる人はちょっとひねくれすぎかと。
とにかく、綺麗に書けるようになったのはすばらしいことです。