【Java学習|初心者向け】Javaのswitch式で必須!「サブクラスを先に書く」ルールの重要性

1. 導入:なぜこのルールが重要なのか?

Java 17で正式導入された「switch式」や「パターンマッチング」を使う際、コンパイラから「サブクラスを先に記述してください」という警告やエラーを受けたことはありませんか?これは、Javaがプログラムの安全性を守るために定めた非常に重要なルールです。このルールを守ることで、予期せぬ実行時エラーを防ぎ、読みやすく堅牢なコードを書くことができるようになります。

2. 基礎知識:なぜサブクラスが先なのか

Javaの継承関係において、サブクラスは親クラス(スーパークラス)の性質をすべて持っています。そのため、もし「先に親クラスを判定」してしまうと、サブクラスも親クラスの条件に該当したとみなされ、以降のサブクラス判定コードが「到達不能なコード(Unreachable code)」として無視されてしまいます。

例えば、「動物(親)」と「犬(子)」という関係で、先に「動物か?」を確認してしまうと、犬も動物であるため、永遠に「犬か?」という判定にたどり着けません。Javaのコンパイラは、このバグを未然に防ぐために「具体的なもの(サブクラス)から判定しなさい」と強制しているのです。

3. 実装/解決策:正しい記述順序

switch式やif-elseブロックを書く際は、継承関係の範囲が狭い順(具体的→抽象的)に記述するのが鉄則です。

  • 悪い例:親クラス → 子クラス(子クラスの判定が実行されない)
  • 良い例:子クラス → 親クラス(具体的なケースを先に処理する)

4. サンプルプログラム

以下のコードは、sealed class(封印されたクラス)を活用したswitch式の例です。これをコピー&ペーストして動作確認してみてください。

public class SwitchExample {
    // 形状を表すインターフェース(親)
    sealed interface Shape permits Circle, Rectangle {}
    
    // 円(子)
    record Circle(double radius) implements Shape {}
    // 四角形(子)
    record Rectangle(double width, double height) implements Shape {}

    public static void main(String[] args) {
        Shape shape = new Circle(5.0);

        // switch式でのパターンマッチング
        String result = switch (shape) {
            // サブクラス(具体的)を先に記述する
            case Circle c -> "円の半径は " + c.radius();
            case Rectangle r -> "四角形の面積は " + (r.width()  r.height());
            // 親クラスを最後に記述する(封印クラスの場合は不要なこともある)
            default -> "不明な形状";
        };

        System.out.println(result);
    }
}

5. 応用・注意点:現場で陥りやすい罠

現場でよくあるミスは、if-else文での条件漏れです。
if文で継承関係を判定する場合、instanceofを使ってチェックしますが、ここでも同様にサブクラスからチェックしてください。

また、最近のJavaでは `sealed class`(permitsキーワードで継承先を制限)を使うと、コンパイラが「どのサブクラスを網羅すべきか」を把握してくれるため、switch式で漏れがあれば教えてくれます。これを利用することで、将来的に新しいサブクラスを追加した際にも、コンパイルエラーによって「修正忘れ」を防ぐことができます。

「とりあえず親クラスを先に書く」という癖がある方は、ぜひ「特殊なケースから先に書く」という意識に変えてみてください。それだけで、コードの品質が一段と向上します。

コメント

タイトルとURLをコピーしました