sealedクラスで表現力は上がるか?
sealedクラスで表現力は本当に上がるのか?
Java 17で正式に導入された sealed
クラス機能は、サブクラスの制限と型の網羅性の明示によって、従来の継承設計に比べて“安全性”と“表現力”が高まったとされます。
しかし現場のコードレビューでは、「sealedを使えば表現力が高い」「型が増えても管理しやすい」といった期待が安易に先行しているケースが多く見受けられます。
本記事では、sealedクラスの正しい設計活用と、レビュー時に警戒すべき設計ミスを整理します。
そもそもsealedクラスとはなにか?
sealedクラスは、どのクラスが継承できるかを明示的に制限できる仕組みです。
public sealed interface Shape
permits Circle, Rectangle {}
public final class Circle implements Shape {}
public final class Rectangle implements Shape {}
これにより以下が保証されます:
- 拡張可能なサブタイプが明示的に制限される
- switch式やパターンマッチングとの親和性が高まる
- 不完全な拡張をコンパイル時に検出可能
non-sealed
を指定することで、継承制限を明示的に解除できます。逆に、final
指定で継承を不可にするのは従来どおりです。
non-sealedによる継承制御の中途緩和
sealed
によって一度制限した型階層も、サブクラス側で non-sealed
を指定することで継承制限を再び開放することができます。
public sealed interface Operation permits Arithmetic, Unknown {}
public non-sealed class Unknown implements Operation {
// このクラスを継承する他クラスがさらに自由に拡張可能になる
}
この仕組みにより、部分的に開放された継承ツリーを構築することが可能になります。
sealed
による制限を段階的に緩和するための構文- 全体をsealedで閉じつつ、特定のノードのみ拡張可能とする設計が可能
しかし、この設計はレビュー観点で注意が必要です。
非sealedなクラスを許容することで、sealed本来の安全性・網羅性が崩れる可能性があるためです。
レビュー観点:non-sealedが設計を壊していないか?
以下のようなケースでは設計の見直しが必要です:
- sealedで閉じたい理由があるにもかかわらず、non-sealedで回避されている
- 将来的な拡張を見越して、とりあえず non-sealed にしている
- 本来 sealed 構造で表現すべきドメインに、non-sealed が混ざっている
`non-sealed`によってsealedの目的損失していないか設計レベルでの再評価が必要。
sealedによって得られる型の網羅性や制御性は、non-sealedの混在によって無効化されるため、必然性のある開放かどうかを常に問う必要があります。
sealedとnon-sealedは「クローズド vs オープン」な構造の切り替え機構です。レビューでは、この切替が意図的かつ整合的に行われているかを確認することが求められます。
sealedとジェネリクス構文の混同に注意
Javaの sealed
クラスを「型制限のための構文」と見なす際、しばしば以下のような誤解が見られます:
sealedは
<A extends B>
のようなテンプレート的な制限を強化した構文である
これは言語機能的には 誤った認識 です。
sealed
: 実際の継承クラスをpermits
で明示的に指定し、階層の拡張自体を制限extends
: 型パラメータの範囲を制限するジェネリクス構文であり、インスタンスや実装の制限は伴わない
典型的な混同例
public class Response<T extends Result> {
private final T value;
}
このようなコードは「許された型だけ使っているように見える」が、T
に何が入ってくるかは制限されていないため、sealed
的な安全性は提供しません。
一方、以下のような sealed の設計は:
permits Success, Failure {}
final class Success implements Result {}
final class Failure implements Result {}
Result型として扱える実体はコンパイル時点で固定されるため、パターンマッチングの網羅チェックなどが有効になります。
レビュー観点:sealedとジェネリクスの混同を検出する
レビュー時には、以下のような設計の混同を見抜くことが求められます:
sealed
が適切にpermits
で明示されているか?- 単なる
<T extends Base>
に過剰な安全性を期待していないか? - ジェネリクスの型パラメータが sealed のように振る舞うと誤認していないか?
`<T extends ...>` にsealedと同様の安全性を期待していないかを確認してください。
このような混乱を防ぐためにも、「型の制約」と「継承の制限」は別物であるという前提を常に意識してレビューすることが重要です。
sealedクラスが得意とする構造パターン
sealedクラスは、設計において以下のような“型を列挙する表現”で効果を発揮します。
- コンパイラレベルでの分岐網羅チェック(exhaustive check)
- インターフェース駆動での制御ロジック
instanceof
やswitch
とのパターンマッチング
Shape shape = ...;
switch (shape) {
case Circle c -> handleCircle(c);
case Rectangle r -> handleRectangle(r);
}
sealedに過剰な期待をかけた設計ミス
1. sealedを使えば将来の設計変更に強くなるという誤解
sealedによりサブタイプが制限されると「安心できる設計」と捉えがちですが、それは現時点でのモデルが正確であることが前提です。
後から新しい種別が必要になったとき、sealedの制約が逆にボトルネックとなり、以下のような悪循環を生む可能性があります:
- 無理に既存型に詰め込む設計(God Type化)
non-sealed
で制限を外すことで安全性が失われる- 一貫性のために既存型全体の再設計が必要
sealedは「将来の型の追加がない」という設計の仮定に依存します。仮定が崩れた瞬間に制約が負債化します。
2. enumの代用としての誤用
sealed classの使い方が enum
の置き換えのようになっているコードも多く見られますが、enum
はより限定的・軽量な手段です。
public sealed interface Status permits Success, Error {}
public final class Success implements Status {}
public final class Error implements Status {}
このようなケースでは enum Status { SUCCESS, ERROR }
のほうが設計的に適切です。
sealedクラスの設計誤用
このように、型の種類が限定的であるなら、sealedよりもenumを選ぶことでシンプルさを保つことができます。
レビューで指摘すべきsealed使用時の注意点
sealedクラスは便利である反面、構造の固定化によって将来の柔軟性を損なうリスクを持ちます。レビュー時には以下を確認すべきです。
チェックリスト:sealed使用のレビュー観点
適切なsealed設計パターン
1. バリエーションのあるオブジェクト設計
public sealed interface Operation permits Add, Subtract, Multiply {}
public final class Add implements Operation {}
public final class Subtract implements Operation {}
public final class Multiply implements Operation {}
このように、同じインターフェースを持つ複数の処理が想定されており、今後も増える予定がないと判断されるとき、sealedは設計として有効です。
sealedを使う前に、その型のバリエーションが将来増える見込みがあるかどうかを、ビジネス的視点で検討してください。
sealedと型安全性
sealedクラスは、パターンマッチングとの組み合わせで型安全性を高めます。
特にIDEによる網羅性チェック、静的解析ツールによる補完性チェックなど、“全パターンの対応漏れ”を防ぐ仕組みと組み合わせると強力です。
例:パターンマッチングとの併用
if (shape instanceof Circle c) {
// ...
} else if (shape instanceof Rectangle r) {
// ...
} // 他の型がないことが保証されている
sealedがもたらす「閉じた世界でのパターン展開」は、データ駆動なロジック設計を強化する一方で、柔軟性の犠牲を伴います。
まとめ:sealedは「表現力の拡張」ではなく「表現の制限」
sealedクラスは、「可能性の絞り込み」によって設計を明快にする構造です。
そのため、以下のようなレビュー指摘が有効です:
- sealedで型を制限することで、今後の変更に弱くなっていないか?
- sealedの使用目的が「安全性」ではなく「過度な制御欲」になっていないか?
- sealedによってコードが複雑化・冗長化していないか?
sealedは適用範囲が狭いがゆえに、うまくはまると設計の堅牢性は増します。ただし、無理に使うと拡張不能な構造になり、長期的な柔軟性を損なうことにもつながります。
レビューアーは「sealedを使った方が良さそう」ではなく、「sealedにすべき必然性があるか」を常に問い直す視点が求められます。