1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Java16]instanceofの型指定に追加される制約

Posted at

新しい構文

Java16(2021/3/16 GA予定) から正式導入される「instanceofのパターンマッチング」(JEP 394),便利そうですよね! よく知らない方のために簡単に紹介しますと,

if (a instanceof Point p) {
    // p を Point 型変数として使える
    ...
}

のように書けます.他にもスコープ関係など細かい仕様があるのですが,詳しい説明は他の記事に譲ることにします(気が向いたら自分で書くかも).

今回は,この記法に関連して気が付いた制約があるので紹介します.

型の制約

if("text" instanceof String) {
    System.out.println("ok");
}

これは以前からコンパイルできますし,予想通りに実行されます.ところが,

if("text" instanceof String s) {
    System.out.println(s);
}

このコードを,Java161でコンパイルしようとすると

Sample.java:5: エラー: パターン・タイプStringは式タイプStringのサブタイプです
        if("text" instanceof String s) {
                  ^
エラー1個
エラー: コンパイルが失敗しました

のようにコンパイルエラーになります2.ちなみに,

if("text" instanceof Number n) {
    System.out.println(n);
}

の場合は

Sample.java:5: エラー: 不適合な型: StringをNumberに変換できません:
        if("text" instanceof Number n) {
           ^
エラー1個
エラー: コンパイルが失敗しました

という別のコンパイルエラーになります.

実は,パターンマッチングの場合に限って,instanceof の次に指定する型には2種類の制約があります.instanceof の左辺のコンパイル時の型をS,右辺に指定する変換先の型をT とすると

  • S型のインスタンスが T 型になる可能性がなければならない3
  • STのサブタイプであってはならない4

つまり,「常にマッチしない」ものも「常にマッチする」ものも禁止されているわけです.

ところが,従来のinstanceof構文には1つ目の制約しかありません.互換性を保つ限り後から制約を追加することは出来ないので,今後はこのままパターンマッチングかどうかで制約度合いが異なる状態が続くのではないかと思います.

では,この制約を加えることでどんなプログラムが規制されるのかという話ですが,筆者が考えた例が1つあるので紹介します.

禁止される例

    private void newStyle(List<String> list) {
        if(!list.isEmpty() && list.get(0) instanceof String s && !s.isEmpty()) {
            // s を使う処理
        }
    }

実は,Java15 の時点では追加制約が無かったので,このコードは--enable-previewフラグを指定すればコンパイルできて,意味的には

    private void oldStyle(List<String> list) {
        if(!list.isEmpty()) {
            String s = list.get(0);
            if(!s.isEmpty()) {
                // s を使う処理
            }
        }
    }

と同じになります.つまり,パターンマッチングを本来の目的外に転用した読みづらいコードが書けてしまうわけですね.

余談ですが,どうしてもネストを減らしたければこんな書き方もできます:

    private void anotherStyle(List<String> list) {
        String s;
        if(!list.isEmpty() && !(s = list.get(0)).isEmpty()) {
            // s を使う処理
        }
    }

これはこれで…笑

おわりに

というわけで,instanceofの型の制約についての話でした.追加制約の理由や分かりやすい例など,他にあればぜひ教えて下さい.

  1. Release Candidate(Build 35) で検証

  2. しかもこのメッセージだと一見何が悪いのか分からない…

  3. 例えば, Number クラスのサブクラスが BufferedReader クラスを継承する可能性はありません.しかし,Number のサブクラスが Closeable インターフェースを実装する可能性はあります.

  4. つまり,TS と一致するか S のスーパークラス/スーパーインターフェースである場合は不可

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?