コトリンの密閉クラス

コトリンの非公開クラス

1. 前書き

簡単に言えば、Kotlin言語は、より安全で読みやすいコードの作成を支援するために、他の関数型言語から多くの概念を取り入れました。 封印された階層は、これらの概念の1つです。

2. 封印されたクラスとは何ですか?

封印されたクラスを使用すると、型階層を修正し、開発者が新しいサブクラスを作成することを禁止できます。

これらは、可能なサブクラスの特定のセットがあり、他のサブクラスがない、非常に厳密な継承階層がある場合に役立ちます。 The compiler guarantees that only classes defined in the same source file as the sealed class are able to inherit from it.

封印されたクラスも暗黙的にabstractです。 他のコードで実装できない場合を除いて、コードの残りの部分ではこれらをそのまま扱う必要があります。

シールクラスには、抽象関数と実装関数の両方を含む、フィールドとメソッドを定義できます。 これは、クラスのベース表現を取得し、サブクラスに合わせて調整できることを意味します。

3. 封印されたクラスをいつ使用するか?

値のSealed classes are designed to be used when there are a very specific set of possible options、およびこれらの各オプションが機能的に異なる場合–Algebraic Data Types.のみ

一般的な使用例には、State MachineまたはMonadic Programmingの実装が含まれる場合があります。これは、関数型プログラミングの概念の出現によりますます一般的になっています。

複数のオプションがあり、それらがデータの意味のみが異なる場合は、代わりにEnum Classesを使用する方がよい場合があります。

不明な数のオプションがある場合は、シールされたクラスを使用できません。これにより、元のソースファイルの外部にオプションを追加できなくなるためです。

4. 封印されたクラスを書く

独自の封印されたクラスを作成することから始めましょう。このような封印された階層の良い例は、Java 8のOptionalです。これはSomeまたはNone.のいずれかです。

これを実装するとき、新しい実装を作成する可能性を制限することは非常に理にかなっています。2つの提供された実装は網羅的であり、誰も独自に追加すべきではありません。

そのため、これを実装できます。

sealed class Optional {
    // ...
    abstract fun isPresent(): Boolean
}

data class Some(val value: V) : Optional() {
    // ...
    override fun isPresent(): Boolean = true
}

class None : Optional() {
    // ...
    override fun isPresent(): Boolean = false
}

これで、Optional<V>のインスタンスがあるときはいつでも、実際にはSome<V>またはNone<V>のいずれかがあることが保証されます。

Java 8では、封印されたクラスがないため、実際の実装は異なって見えます。

その後、計算でこれを使用できます。

val result: Optional = divide(1, 0)
println(result.isPresent())
if (result is Some) {
    println(result.value)
}

最初の行は、SomeまたはNoneのいずれかを返します。 次に、結果が得られたかどうかを出力します。

5. いつで使用する

Kotlinは、when構造で封印されたクラスを使用することをサポートしています。 可能なサブクラスの正確なセットが常に存在するため、the compiler is able to warn you if any branch is not handled, in exactly the same way that it does for enumerations.

つまり、このような状況では、通常、キャッチオールハンドラーは必要ありません。つまり、新しいサブクラスを追加しても自動的に安全です。コンパイラーは、処理していない場合はすぐに警告を発し、必要になります。続行する前にそのようなエラーを修正します。

上記の例を拡張して、返されるタイプに応じて結果またはエラーを出力できます。

val message = when (result) {
    is Some -> "Answer: ${result.value}"
    is None -> "No result"
}
println(message)

2つのブランチのいずれかが欠落している場合、これはコンパイルされず、代わりに次のエラーが発生します。

'when' expression must be exhaustive, add necessary 'else' branch

6. 概要

シールクラスは、APIデザインツールボックスにとって非常に貴重なツールになります。 予想されるクラスのセットの1つにしかならない、よく知られた構造化されたクラス階層を許可すると、コードから潜在的なエラー条件のセット全体を削除しながら、読みやすく保守しやすくなります。

いつものように、コードスニペットはover on GitHubで見つけることができます。