新しい構文
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
とすると
つまり,「常にマッチしない」ものも「常にマッチする」ものも禁止されているわけです.
ところが,従来の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
の型の制約についての話でした.追加制約の理由や分かりやすい例など,他にあればぜひ教えて下さい.