Kotlin Sealed クラス

このチュートリアルでは、KotlinのSealed Classについて説明します。それらは何ですか?どのように使用されますか?以下でこれらのすべてについて説明します。

KotlinのSealed Class

素人の言葉で言えば、その名前が示すように、シールドクラスはシールドまたは閉じられており、制限されています。シールドクラスは、オブジェクトまたは値が一つのタイプのみで値を持つ制限されたクラス階層を表すために使用されます。したがって、タイプ階層が固定されます。シールドクラスは、与えられた値が与えられたオプションのセットの中でのみあることが分かっている場合に一般的に使用されます。

Kotlinのシールドクラスの実装

Kotlinでのシールドクラスの実装方法は次のようになります。

sealed class A{
    class B : A()
    class C : A()
}

シールドクラスを指定するには、修飾子sealedを追加する必要があります。シールドクラスはインスタンス化できません。したがって、暗黙的に抽象です。以下は動作しません。

fun main(args: Array<String>) 
{
    var a = A() //compiler error. Class A cannot be instantiated.
}

シールドクラスのコンストラクタはデフォルトで非公開です。シールドクラスのすべてのサブクラスは同じファイル内で宣言する必要があります。シールドクラスは、コンパイル時にのみタイプのセットを制限することで、型の安全性を確保する上で重要です。

sealed class A{
    class B : A() 
    {
        class E : A() //this works.
    }
    class C : A()

    init {
        println("sealed class A")
    }

}

class D : A() //this works
{
class F: A() //This won't work. Since sealed class is defined in another scope.
}

コンストラクタを持つ密封クラスの作成。

sealed class A(var name: String){
    class B : A("B")
    class C : A("C")
}

class D : A("D")
fun main(args: Array<String>) {
    
    var b = A.B()
    var d = D()
}

密封クラスにDataクラスObjectを追加します。

fun main(args: Array<String>) {

    val e = A.E("Anupam")
    println(e) //prints E(name=Anupam)

    var d = A.D
    d.name() //prints Object D
}


sealed class A{
    class B : A()
    class C : A()
    object D : A()
    {
         fun name()
         {
             println("Object D")
         }
    }
    data class E(var name: String) : A()

}

列挙型と密封クラスの違い

Kotlinでは、密封クラスはエンムクラスのスーパーセットとして考えることができます。密封クラスでは、エンムクラスとは異なり、異なる型のインスタンスを作成することができます。エンムでは、すべての定数に同じ型を使用することが制限されていますが、密封クラスでは複数のインスタンスを許可します。

enum class Months(string: String){
January("Jan"), February(2),
}

エンムクラスでは、すべての定数に対して単一の型のみが許可されます。これが、密封クラスが複数のインスタンスを許可するために役立つ場面です。

sealed class Months {
    class January(var shortHand: String) : Months()
    class February(var number: Int) : Months()
    class March(var shortHand: String, var number: Int) : Months()
}

この密封クラスの機能をプロジェクトでどのように使用できますか?ニュースフィードのようなアプリケーションでは、以下のようにステータス、画像、ビデオの投稿に対して3つの異なるクラスタイプを作成することができます。

sealed class Post
{
    class Status(var text: String) : Post()
    class Image(var url: String, var caption: String) : Post()
    class Video(var url: String, var timeDuration: Int, var encoding: String): Post()
}

これはエンムクラスでは不可能です。

密封クラスとwhen

シールドクラスは、各サブクラスとその型がケースとして機能するため、when文と一緒によく使用されます。さらに、シールドクラスは型を制限することがわかっています。したがって、when文のelse部分は簡単に削除できます。以下の例は、同じことを示しています。

sealed class Shape{
    class Circle(var radius: Float): Shape()
    class Square(var length: Int): Shape()
    class Rectangle(var length: Int, var breadth: Int): Shape()
}

fun eval(e: Shape) =
        when (e) {
            is Shape.Circle -> println("Circle area is ${3.14*e.radius*e.radius}")
            is Shape.Square -> println("Square area is ${e.length*e.length}")
            is Shape.Rectangle -> println("Rectagle area is ${e.length*e.breadth}")
        }

次のように、main関数でeval関数を実行しましょう。

fun main(args: Array) {

    var circle = Shape.Circle(4.5f)
    var square = Shape.Square(4)
    var rectangle = Shape.Rectangle(4,5)

    eval(circle)
    eval(square)
    eval(rectangle)
    //eval(x) //コンパイルエラー。

}

//以下はコンソールに出力されます:
//Circle area is 63.585
//Square area is 16
//Rectangle area is 20

注意: is修飾子は、クラスが次の型であるかどうかをチェックします。 is修飾子は、Kotlinオブジェクトではなくクラスにのみ必要です。以下に示します:

sealed class Shape{
    class Circle(var radius: Float): Shape()
    class Square(var length: Int): Shape()
    object Rectangle: Shape()
    {
        var length: Int = 0
        var breadth : Int = 0
    }
}

fun eval(e: Shape) =
        when (e) {
            is Shape.Circle -> println("Circle area is ${3.14*e.radius*e.radius}")
            is Shape.Square -> println("Square area is ${e.length*e.length}")
            Shape.Rectangle -> println("Rectangle area is ${Shape.Rectangle.length*Shape.Rectangle.breadth}")
        }

これで、Kotlinシールドクラスのチュートリアルは終了です。参考文献: Kotlin Docs

Source:
https://www.digitalocean.com/community/tutorials/kotlin-sealed-class