1. コンセプト
Kotlin の Sealed クラスは、クラスの継承構造を制限するために使用される特別なクラスです。シールされたクラスを使用すると、関連するクラスのセットを定義し、それらのクラスのみがサブクラスとして存在できるようになります。この制限はコンパイル時にチェックできるため、プログラム エラーの可能性が低くなります。
構文は次のとおりです。
sealed class MyClass {
// ...
}
Sealed クラスは、多くの場合、列挙型クラスの代わりに使用されます。シールされたクラスの利点は、列挙型クラスの各メンバーが固定されているのに対し、サブクラスをより柔軟に定義できることです。Sealed クラスは、よりタイプセーフなコードを作成するのにも役立ちます。
2. 応用シナリオ
シールされたクラスの主な使用例は、限られた数の型を表現する必要がある場合です。クラスの考えられるすべてのサブクラスがわかっている場合は、シールされたクラスを使用してそれらのサブクラスを表すことができます。
たとえば、さまざまなタスクの状態を表すシールされたクラスがある場合、さまざまな状態を処理する式をTaskState
作成できます。when
/**
* 密封类: 任务的状态
*/
sealed class SealedTaskState {
// 空闲
object EMPTY : SealedTaskState()
// 执行中
object RUNNING : SealedTaskState()
// 暂停中
object PAUSE : SealedTaskState()
}
// 单元测试
@Test
fun test(sealedTaskState: SealedTaskState) {
when (sealedTaskState) {
// SealedTaskState有3种状态
// 下面的数据必须包含所有的SealedTaskState里的状态,不然编译器会报错
is SealedTaskState.EMPTY -> {
println("空闲")
}
is SealedTaskState.RUNNING -> {
println("执行中")
}
is SealedTaskState.PAUSE -> {
println("暂停")
}
}
}
上記の例では、SealedTaskState のすべての状態発生が処理されるときに、タスクの状態をカプセル化するためにシールド クラスが使用されます。
3. 通常のカテゴリーとの違い
class SealedTest {
// 密封类测试
@Test
fun testSealedShape(sealedShape: SealedShape) {
when (sealedShape) {
is SealedCircle -> println("circle area:${
sealedShape.getArea()}")
// 'when' expression must be exhaustive, add necessary 'is SealedRectangle' branch or 'else' branch instead
// 直接编译报错,提示必须要SealedRectangle分支或者else分支
// is SealedRectangle -> println("rectangle area : ${sealedShape.getArea()}")
}
}
// 普通类测试
@Test
fun testNormalShape(normalShape: NormalShape) {
when (normalShape) {
is NormalCircle -> println("circle area:${
normalShape.getArea()}")
// 可能出现漏掉判断的情况,比如这里,我们把普通类矩形注释了,IDE也没有提示报错
// 但是在密封类中,在编译期我们就能看到错误
// is NormalRectangle -> println("rectangle area : ${normalShape.getArea()}")
}
}
}
/**
* 普通类: 形状
*/
abstract class NormalShape {
// 计算面积
abstract fun getArea(): Double
}
// 普通类: 圆形
class NormalCircle(private val radius: Double) : NormalShape() {
override fun getArea(): Double {
return Math.PI * radius * radius
}
}
// 普通类: 矩形
class NormalRectangle(private val width: Double, private val height: Double) : NormalShape() {
override fun getArea(): Double {
return width * height
}
}
/**
* 密封类: 形状
*/
sealed class SealedShape {
// 计算面积
abstract fun getArea(): Double
}
// 密封类: 圆形
class SealedCircle(private val radius: Double) : SealedShape() {
override fun getArea(): Double {
return Math.PI * radius * radius
}
}
// 密封列: 矩形
class SealedRectangle(private val width: Double, private val height: Double) : SealedShape() {
override fun getArea(): Double {
return width * height
}
}
まず、シールされたクラスを使用せずにNormalShape
、台形や正方形などの新しい形状タイプを追加する場合、testSealedShape
これらの新しいタイプの形状を処理できるように関数を変更する必要があります。次に、when
式で特定の種類の形状を省略すると、コンパイル時または実行時にこのエラーを検出できなくなり、プログラムがその種類の形状を処理するときに例外が発生する可能性があります。
はシールされたクラスであり、そのサブクラスは制限されているためSealedShape
、コンパイラはwhen
式がすべての考えられるケースを処理することを保証できます。新しいグラフィックス タイプを追加する場合は、新しいシールされたサブクラスを追加してgetArea()
メソッドを実装する必要があります。このプロセスは、実行時ではなくコンパイル時にエラーを検出します。これは Sealed クラスの利点の 1 つです。
4. まとめ
上記の例から判断すると、クラスをシールする利点は次のように要約できます。
- コンパイル時のチェック: シールされたクラスのサブクラスは制限されているため、コンパイラは
when
式がすべての考えられるケースをカバーしているかどうかをチェックできます。これにより、実行時に未処理の状況が検出されることが回避され、コードの信頼性と安全性が高まります。 - クラス階層の拡張性: 既存のクラス階層は、既存のコードを変更せずに、新しいシールされたサブクラスを追加することで簡単に拡張できます。これにより、コードの保守性とスケーラビリティが向上します。
- よりクリーンなコード: シールされたクラスにより、コードがより明確になり、理解しやすくなります。これにより、プログラマーはコードをより適切に整理し、理解と変更が容易になります。
全体として、シールされたクラスは、コードの信頼性、保守性、拡張性を向上させることができる非常に便利な言語機能です。限られた数の可能なケースを処理する必要があり、コンパイル時に未処理のケースを検出したい場合は、シールされたクラスが適しています。