導入:なぜこの技術が重要なのか
Javaの設計において、「継承を制御する」ことは堅牢なアプリケーションを作るための鍵です。従来、継承はアクセス修飾子(publicやprotected)で制御してきましたが、これでは「特定のクラスだけ継承させたい」という細かい要件を満たせませんでした。Java 17で導入されたSealed Classes(封印されたクラス)と、その「暗黙の許可(Implicit Permits)」ルールを活用することで、コードの安全性と可読性が劇的に向上します。特に、同一ファイル内でサブクラスを定義する場合、記述を簡素化できる点は現場のエンジニアにとって大きなメリットです。
基礎知識:Sealed Classesとは
Sealed Classesは、どのクラスがそのクラスを継承できるかを明示的に制限する機能です。これにより、意図しないクラスによる継承を防ぎ、網羅的な型チェックが可能になります。
「暗黙の許可(Implicit Permits)」とは、sealedクラスと同じソースファイル内にサブクラスを記述する場合、permits句を省略できる仕組みのことです。これにより、ファイル内で関連するクラスを完結させることができ、保守性が高まります。
実装:暗黙の許可を活用する手順
実装は非常にシンプルです。親クラスにsealed修飾子を付与し、子クラスを同一ファイル内に定義するだけです。これにより、コンパイラは「そのファイル内のクラスだけがサブクラスである」と自動的に認識します。この設計は、特にドメインモデルの表現や、状態遷移の管理において非常に強力な効果を発揮します。
サンプルプログラム
以下のコードは、支払い方法を管理するシステムを想定した例です。同一ファイル内に記述することで、permits句を省略しつつ、安全に継承を制限しています。
// 支払いインターフェースをsealedで定義
sealed interface PaymentMethod {
void process();
}
// 同一ファイル内であれば、permits句なしで継承可能(暗黙の許可)
final class CreditCardPayment implements PaymentMethod {
@Override
public void process() {
System.out.println(“クレジットカードで決済します。”);
}
}
final class CashPayment implements PaymentMethod {
@Override
public void process() {
System.out.println(“現金で決済します。”);
}
}
public class Main {
public static void main(String[] args) {
PaymentMethod payment = new CreditCardPayment();
// switch式と組み合わせることで、網羅的チェックが可能
String result = switch (payment) {
case CreditCardPayment c -> “カード払いを選択しました”;
case CashPayment c -> “現金払いを選択しました”;
// sealedのおかげでdefault句が不要になります
};
System.out.println(result);
}
}
応用・注意点:現場での活用と落とし穴
1. 網羅性チェックの恩恵:
Sealed Classesをswitch式と組み合わせると、コンパイラがすべてのサブクラスが網羅されているかをチェックしてくれます。もし将来的に新しい決済方法を追加した際、switch式にケースを追加し忘れるとコンパイルエラーになるため、バグを未然に防ぐことができます。
2. ファイル分割時のルール:
もしサブクラスが肥大化し、別のファイルに分けたい場合は、親クラスに必ず permits句 を記述する必要があります。この場合、暗黙の許可は無効になるため注意してください。
3. 継承の制限:
サブクラス側には final、sealed、または non-sealed のいずれかを明示する必要があります。特に意図せず継承を許可してしまわないよう、原則としてfinalを指定する癖をつけておくことが、堅牢な設計の第一歩です。

コメント