ProgrammingKotlin開発者

Kotlinにおけるシールドインターフェース(sealed interfaces)とは何か、実際にはどのように機能し、シールドクラスと何が異なるのか?

Hintsage AIアシスタントで面接を突破

回答

問題の歴史

Kotlinの初期バージョンでは、型階層を制限するためにシールドクラスが使用されており、開発者が同じファイル内でのみ継承者の宣言を制限することによって許可されるサブタイプを明示的に制御できました。しかし、インターフェースを使用して類似の階層を説明する必要がある場合には、これは不十分でした。この問題を解決するために、Kotlin 1.5からインターフェース用のsealed修飾子が導入されました。

問題

通常のインターフェースはプロジェクトの任意の場所で実装できるため、型階層の絶対的な閉鎖性を確保し、包括的な型チェック(exhaustive when)を使用することがしばしば妨げられます。これにより、ランタイムエラーやすべてのケースを静的にチェックできないことにつながる可能性があります。

解決策

シールドインターフェースを使用すると、実装クラスのリストを同じファイル内に制限できるため、型システムの予測可能性を向上させ、パターンマッチングの安全性を高めることができます。

sealed interface NetworkResult class Success(val data: String): NetworkResult class Failure(val error: Throwable): NetworkResult fun handle(result: NetworkResult) = when(result) { is Success -> println("Data: ${result.data}") is Failure -> println("Error: ${result.error}") // すべてのケースが考慮されているので、'else'は不要 }

主な特徴:

  • シールドインターフェースは単一階層を許可する:継承できるのは同じファイル内のクラス/オブジェクトのみ
  • シールドインターフェースはクラスおよびオブジェクトとして実装できる
  • 'else'を明示的に考慮せずにwhen表現の型安全性を拡張する

逆質問

シールドインターフェースを別のファイルに追加したり、元のファイルの外で実装したりできますか?

いいえ。シールドインターフェースを直接実装するすべての型は同じファイルで宣言されている必要があります。そうしないと、コンパイラーがエラーを出します。

シールドインターフェースは宣言ファイルの外にサブインターフェースを持つことができますか?

いいえ。サブインターフェースも同じファイル内に存在する必要があります。すべての階層はファイル内で閉じられています。

シールドインターフェースはランタイムパフォーマンスに影響を与えますか?

直接的にはそうではありませんが、コンパイル時に安全な型チェックを使用できるため、ランタイムエラーの可能性を減少させ、コードを簡素化し、テスト速度を間接的に向上させる可能性があります。

よくある間違いやアンチパターン

  • シールドインターフェースの実装クラスをインターフェースが宣言されているファイルの外で宣言すること
  • あまりにも多くのケースを追加すること、メンテナンスを困難にする
  • 選択肢の階層が変更される可能性がある場合にシールドインターフェースを使用する(オプションの閉鎖は必要ない)

実際の例

ネガティブケース

開発者が1つのファイルでシールドインターフェースNetworkActionを宣言するが、実装が異なるクラスやファイルに分散している。問題が遅れて発生する:コンパイラーがルール違反を警告し、構造を修正するのが難しい、特に大規模プロジェクトで特に厄介。

利点:

  • 1つのファイルを編集せずに新しい実装を宣言できる

欠点:

  • 型安全性の保証を侵害する
  • コードをリファクタリングする必要があるため、時間がかかる

ポジティブケース

シールドインターフェースOrderResultとクラスSuccess/Failureが1つのファイル内で宣言される。whenチェックは常に包括的で、すべてのブランチが考慮されている。

利点:

  • 安全性と完全性の処理
  • アーキテクチャの品質を向上させる

欠点:

  • 拡張性が低下する(すべての変更はこのファイルを通じてのみ)