Класс Sealed в Kotlin

В этом уроке мы рассмотрим Kotlin Sealed Class. Что это такое? Какова их цель? Все эти вопросы мы рассмотрим ниже.

Kotlin Sealed Class

Простыми словами, как подсказывает название, sealed classes (запечатанные классы) запечатаны или закрыты, что делает их ограниченными. Запечатанные классы используются для представления ограниченных иерархий классов, где объект или значение может иметь значение только одного из типов, тем самым фиксируя вашу иерархию типов. Запечатанные классы обычно используются в случаях, когда вы знаете, что заданное значение может принадлежать только к определенному набору вариантов.

Реализация Kotlin Sealed Classes

Запечатанные классы в 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()
}

Добавление Класса данных и Объекта в запечатанный класс.

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()
}

Как можно использовать эту функцию запечатанных классов в ваших проектах? В приложении типа новостной ленты вы можете создать три различных типа классов для сообщений о статусе, изображениях, видео, как показано ниже.

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 выражениях, поскольку каждый из подклассов и их типы действуют как случай. Кроме того, мы знаем, что запечатанный класс ограничивает типы. Таким образом, часть else выражения when можно легко удалить. Ниже приведен пример, демонстрирующий то же самое.

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}")
        }

Давайте выполним функцию eval в нашей функции main, как показано ниже.

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) //ошибка времени компиляции.

}

//На консоль выводится следующее:
//Площадь круга составляет 63.585
//Площадь квадрата составляет 16
//Площадь прямоугольника составляет 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

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