JAVA

Java 21で正式機能のsealed classとは?導入と活用ガイド

ⓘ本ページはプロモーションが含まれています

スポンサードリンク

背景 ― JEP 409 が正式機能になるまでの流れ

JEP 409 とは何か

JEP 409 Sealed Classes は、型安全な継承階層を構築するために sealed キーワードと permits リスト、そして non‑sealed キーワードという三つの要素で成り立っています。

  • sealed … 継承できる直接サブクラスをコンパイラが検証
  • permits … 許可されたサブクラスを列挙(カンマ区切り)
  • non‑sealed … sealed 階層から抜け、自由に継承可能にする

この仕組みは Java 17 でプレビューとして導入され、Kotlin の sealed クラスや Scala の final/sealed と同様の安全性を提供すると評価されました。

正式化に至った主な理由

要因 詳細
大規模プロジェクトでの継承誤用防止 企業レベルのコードベースでは、意図しないサブクラスが増えるとバグやセキュリティリスクが顕在化します。sealed により「許可された型だけ」が明示的に管理できます。
他言語との互換性確保 Kotlin・Scala の開発者からは、同等の機能を Java でも欲しいという要望が多数寄せられました。
コンパイラとランタイムの二重検証 JEP 409 は javac と JVM の両方で許可リストの整合性をチェックする設計です。この二段階検証により、実行時エラーがほぼ排除されます。
実装コストの低減 既存の abstract クラスを sealed に変えるだけで機能が有効になるため、移行コストが低く抑えられました。

Oracle の公式ドキュメント(Sealed Classes and Interfaces – Java 21)でも「コンパイル時に許可されたすべてのサブタイプへアクセスできることが前提」と明記されています。

:本稿中で参照している Qiita 記事(Java 21新機能まとめ #OpenJDK)は個人の解釈に基づく情報です。公式仕様が将来的に変更される可能性がありますので、最新情報は必ず Oracle のドキュメントをご確認ください。


基本構文とコンパイル要件

sealed, permits, non‑sealed キーワードの正しい使い方

修飾子 意味
sealed 直接のサブタイプは permits で列挙されたものだけに限定されます。
permits 許可クラスをカンマ区切りで列挙します。同一モジュールか、適切にエクスポートされた別モジュールに存在する必要があります。
final 継承不可。最終的な実装クラスに付与します。
non‑sealed sealed 階層から抜け、以降は自由に継承可能です。

実装例

module-info.java における可視性設定

sealed クラスとその許可クラスは 同一モジュール に置くか、別モジュールの場合は明示的にエクスポートしてアクセス可能にしなければコンパイルエラーになります。

別モジュールへ分割したいケース:

上記のように exports … to で対象モジュールを絞ることで、内部実装のカプセル化sealed の検証要件 を同時に満たすことができます。


Pattern Matching for switch との相乗効果

型安全な switch 式の書き方

Java 21 では Pattern Matching for switch が正式機能となり、sealed 階層と組み合わせると 網羅性チェック がコンパイラレベルで保証されます。

  • case Circle c などの 型パターンShape の直接サブタイプとして認識され、すべて列挙しないと “switch expression does not cover all possible input values” エラーになります。
  • 将来的に Squarenon‑sealed にしたうえで新しいサブクラス Triangle が追加された場合、IDE とコンパイラが 未網羅 警告を出すため、忘れが防げます。

IDE のコード補完とリファクタリング支援

IDE 補完機能のポイント
IntelliJ IDEA (2023.2 以降) switch (shape) { と入力すると自動で case Circle c ->, case Square s -> 等がポップアップ。未網羅ケースは Quick Fix から自動生成可能。
Eclipse (2023‑12) 「Ctrl+1」や「Quick Fix」で Missing case を検出し、テンプレートコードを挿入できる。

このように sealed が提供する型情報は IDE の解析エンジンに直接利用されるため、手書きミスが大幅に減少します。


ビルド・IDE 設定とサンプル実装(Shape 階層)

Maven と Gradle での標準設定

--enable-preview フラグは不要です。Java 21 をコンパイル対象に指定すれば sealed クラスはそのまま利用できます。

Maven (pom.xml)

Gradle (build.gradle)

IDE の最小構成チェックリスト

IDE 必要な設定項目
IntelliJ IDEA Project Structure → Project SDK を JDK 21、Language level も 21 に設定。Enable preview features は OFF(不要)。
Eclipse Preferences → Java → Compiler → JDK Compliance を 21 にし、Enable preview features のチェックは外す。

完全版サンプルコード

ポイント
- Shape が sealed であるため、switch 式は必ずすべてのサブタイプを列挙しなければコンパイルできません。
- Square を non‑sealed にしたことで、別モジュールが Triangle extends Square のように拡張可能です。その際は module-info.java でエクスポート設定が必要になります。


移行ガイド・ベストプラクティス & アンチパターン

既存継承階層を段階的に sealed 化する手順

  1. 対象クラスを abstract に変更し、sealed 修飾子と permits を付与
    java
    // 変更前
    public abstract class Message {}

// 変更後
public sealed abstract class Message permits TextMessage, ImageMessage {}

2. **サブクラスを
finalまたはnon‑sealedに変換**
3. **module-info が存在すればエクスポート設定を見直す**(同一モジュールなら不要)
4. **テストスイートでコンパイルエラーが出ないことを確認**。特に
switch
文の網羅性チェックは移行後に自動的に有効になるので、失敗したテストは即座に原因が判明します。

このプロセスは「安全な小さなステップ」で進められるため、リリースサイクルへの影響を最小限に抑えられます。

許可クラスは 最小限 に保つべき理由

目的 推奨策
カプセル化 permits には外部公開したいサブタイプだけを列挙し、内部実装は non‑sealed にしてモジュール内に閉じ込める。
保守性向上 許可リストが長くなると追加・削除の影響範囲が拡大します。できるだけ API レイヤー実装レイヤー を分離し、permits の対象は API に限定する。
コンパイルエラーの検出効率化 変更があった際にコンパイラが即座に警告を出すので、意図しない依存関係が早期に判明します。

アンチパターン例(過剰な permits 列挙)

  • 問題点
  • 公開 API が多数の実装に依存し、内部変更が外部へ波及しやすい。
  • 新しい実装を追加するたびに permits を更新しなければならず、ミスが起きやすくなる。

  • 改善策

  • Service 自体はインタフェースか抽象クラスとして sealed にせず、公開 API 用の public interface Service とし、実装側を別パッケージに置いて non‑sealed にする。

ベストプラクティスまとめ

項目 推奨手法
許可リスト 公開向けサブタイプのみ列挙し、内部実装は non‑sealed にしてモジュール内に閉じ込める。
モジュール化 同一モジュールにまとめるか、exports … to で必要最小限の可視性を付与する。
テスト戦略 switch 式の網羅性チェックを利用し、変更時にコンパイルエラーが出ることを安全装置として活用。
ドキュメント permits に列挙したクラスの役割・使用範囲は Javadoc で必ず記述する。
IDE 活用 IDE のコード補完と未網羅警告を積極的に利用し、手書きミスを防止する。

まとめと導入の呼びかけ

  • Java 21 で sealed class が正式機能化(JEP 409)し、継承制御が言語レベルで保証されます。
  • sealed, permits, non‑sealed の構文と module-info.java によるアクセス設定を正しく行うだけで、コンパイルエラーや実行時不整合を防げます。
  • Pattern Matching for switch と組み合わせることで、網羅性がコンパイラにチェックされ、コードの安全性と可読性が大幅に向上します。
  • Maven / Gradle の標準設定と最新 IDE(IntelliJ IDEA 2023.2+, Eclipse 2023‑12)を活用すれば、開発フローへの影響は最小です。
  • 既存プロジェクトへの 段階的移行許可クラスの最小化 を守ることで、リファクタリングコストやバグリスクを抑制できます。

ぜひ 本ガイドに沿って sealed classpattern matching for switch をプロジェクトへ導入し、型安全な継承階層とメンテナンス性の高いコードベースを実現してください。


スポンサードリンク

-JAVA
-, , , , , ,