Help us understand the problem. What is going on with this article?

Java12が出たので、とりあえずswitch式を試してみた

概要

Java12が出ましたね。変更点、新機能は色々あると思いますが、最もわかりやすい変更点であるswitch文(あるいはswitch式)について試してみました。

注意点

新しいswitch文の書き方は、Java12時点ではプレビュー機能という位置付けらしく、コンパイル時と実行時にオプションを付けないといけません。

コンパイル時

javac --enable-preview --release 12  MultiCaseSample.java

実行時
java --enable-preview MultiCaseSample

また、ここに書いた内容は将来変更される可能性があります。

内容

複数case

case節にカンマ区切りで複数の値を指定できます。

public class MultiCaseSample {
    public static void main(String[] args) {
        var month = Integer.parseInt(args[0]);

        switch(month) {
            case 1, 2:
                System.out.println("冬ですね");
                break;
            case 3, 4, 5:
                System.out.println("春ですよー");
                break;
            case 6, 7, 8:
                System.out.println("夏真っ盛り");
                break;
            case 9, 10, 11:
                System.out.println("俺たちの秋はこれからだ!");
                break;
            case 12:
                System.out.println("年末なので来年からほんきをだす");
                break;
            default:
                System.out.println("そんな月ありません");
        }
    }
}

アロー構文

「->」という記号の後に文を書きます。コロンとbreakの記述を省略できます。
ブロックを使えば複数行の処理を書けるそうです。(下の例だと一行ですが…)
あと、ブロックの閉じカッコの後にセミコロンは不要なのですね。

public class ArrowSample {
    public static void main(String[] args) {
        var month = Integer.parseInt(args[0]);

        switch(month) {
            case 1, 2 -> System.out.println("冬ですね");
            case 3, 4, 5 -> System.out.println("春ですよー");
            case 6, 7, 8 -> System.out.println("夏真っ盛り");
            case 9, 10, 11 -> System.out.println("俺たちの秋はこれからだ!");
            case 12 -> System.out.println("年末なので来年からほんきをだす");
            default -> {
                System.out.println("そんな月ありません");
            }
        }
    }
}

switch式

switch文はswitch式になりました。文と式の違いは、前者は値を返さないのに対し、後者は値を返すという点です。なので、switch式の評価結果をそのまま変数に代入できますね。
この場合は、switchの閉じカッコの後にセミコロンが必要になります。

また、全てのパターンを網羅していないと(この例の場合はdefault節がないと)コンパイルエラーになります。暗黙的にnullが代入されるとかよりは、そっちのほうがいいですね。
アロー構文を使わないか、ブロックを使う書き方だと、返す値をbreakの後に記述するそうです。

public class SwitchExpressionSample {
    public static void main(String[] args) {
        var month = Integer.parseInt(args[0]);

        var message = switch(month) {
            case 1, 2 -> "冬ですね";
            case 3, 4, 5 -> "春ですよー";
            case 6, 7, 8 -> {
                break "夏真っ盛り";
            }
            case 9, 10, 11 -> "俺たちの秋はこれからだ!";
            case 12 -> "年末なので来年からほんきをだす";
            default -> "そんな月ありません";
        };
        System.out.println(message);
    }
}

どうも、アロー構文と従来の構文を混ぜるとコンパイルエラーになってしまうようです。
なのでアロー構文を使わないパターンは別途書きました。

public class SwitchExpressionBreakSample {
    public static void main(String[] args) {
        var month = Integer.parseInt(args[0]);

        var message = switch(month) {
            case 1, 2:
                break "冬ですね";
            case 3, 4, 5:
                break "春ですよー";
            case 6, 7, 8:
                break "夏真っ盛り";
            case 9, 10, 11:
                break "俺たちの秋はこれからだ!";
            case 12:
                break "年末なので来年からほんきをだす";
            default:
                break "そんな月ありません";
        };
        System.out.println(message);
    }
}

その他、気になったこと

Enumで全ての要素を列挙する場合はdefault不要か?

defaultがなくても、コンパイル時、実行時ともにエラーはなく動作しました。

public class SwitchExpressionEnumSample {

    private enum Month {
        JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE,
        JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER,
    }

    public static void main(String[] args) {
        var month = Month.values()[Integer.parseInt(args[0])];

        var message = switch(month) {
            case JANUARY, FEBRUARY -> "冬ですね";
            case MARCH, APRIL, MAY -> "春ですよー";
            case JUNE, JULY, AUGUST -> "夏真っ盛り";
            case SEPTEMBER, OCTOBER, NOVEMBER -> "俺たちの秋はこれからだ!";
            case DECEMBER -> "年末なので来年からほんきをだす";
        };
        System.out.println(message);
    }
}

そもそも、switchの条件がnullだったらどうなるか

今回のリリースとは関係なく以前からありうるパターンですが、上記のEnumの例だとnullの場合のcaseがなく、それで網羅されていると言えるのか気になりました。

public class SwitchExpressionNullSample {

    private enum Month {
        JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE,
        JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER,
    }

    public static void main(String[] args) {
        Month month = null;

        var message = switch(month) {
            case JANUARY, FEBRUARY -> "冬ですね";
            case MARCH, APRIL, MAY -> "春ですよー";
            case JUNE, JULY, AUGUST -> "夏真っ盛り";
            case SEPTEMBER, OCTOBER, NOVEMBER -> "俺たちの秋はこれからだ!";
            case DECEMBER -> "年末なので来年からほんきをだす";
        };
        System.out.println(message);
    }
}

結果は下記の通りでした。(switch(month)の部分でぬるぽ)
まあ、そうでしょうね。

Exception in thread "main" java.lang.NullPointerException
        at SwitchExpressionNullSample.main(SwitchExpressionNullSample.java:11)

(2019/5/21追記)Enumで全ての要素を網羅していない場合はエラーとなるか?

肝心のパターンが漏れていました…
適当にコメントアウトして、どうなるか試してみます。

public class SwitchExpressionEnumSample {

    private enum Month {
        JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE,
        JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER,
    }

    public static void main(String[] args) {
        var month = Month.values()[Integer.parseInt(args[0])];

        var message = switch(month) {
            case JANUARY, FEBRUARY -> "冬ですね";
            case MARCH, APRIL, MAY -> "春ですよー";
            case JUNE, JULY, AUGUST -> "夏真っ盛り";
            case SEPTEMBER, OCTOBER, NOVEMBER -> "俺たちの秋はこれからだ!";
        //    case DECEMBER -> "年末なので来年からほんきをだす";
        };
        System.out.println(message);
    }
}

結果は下記の通り。やはりコンパイルエラーとなるようです。漏れによるバグを防いでくれるので助かりますね。
具体的にどの要素が漏れているのかまでは教えてくれないようですが。

SwitchExpressionEnumSample.java:11: エラー: switch式がすべての可能な入力値をカバーしていません
        var message = switch(month) {
                      ^

(2019/9/26追記)Java13での変更点

Java13がリリースされましたが、Switch式の機能はプレビューのままです。ただ、文法に若干変更点があったので追記します。

アロー構文を使わないか、ブロックを使う書き方だと、返す値をbreakの後に記述するそうです。

Java13ではbreakではなくyieldを使うように変更されました。

public class SwitchExpressionSample {
    public static void main(String[] args) {
        var month = Integer.parseInt(args[0]);

        var message = switch(month) {
            case 1, 2 -> "冬ですね";
            case 3, 4, 5 -> "春ですよー";
            case 6, 7, 8 -> {
                yield "夏真っ盛り";
                // ↑これ
            }
            case 9, 10, 11 -> "俺たちの秋はこれからだ!";
            case 12 -> "年末なので来年からほんきをだす";
            default -> "そんな月ありません";
        };
        System.out.println(message);
    }
}

(2020/3/18追記)Java14リリース

Java14がリリースされ、switch式は正式な機能になりました。仕様はどうやらJava13から変更はなさそうです。

感想

便利だと思いました。(小並)
アロー構文を使えばbreak漏れによるバグを防げますし、switch式の結果をそのまま代入すれば、値のパターン漏れによるバグを防げます。
また、従来はbreakをあえて書かずにfallthroughさせるテクニックがあり、アロー構文を使う場合はそれができなくなりますが、caseの複数指定ができるようになったので特に困らないと思います。

参考リンク

dhirabayashi
しがないプログラマ(主にJava)
https://dhirabayashi.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした