Sealed class and interface
Sealed class and interface

Sealed class and interface

提供方: JetBrains 学院

你还记得 枚举 吗?枚举是用来存放 常量 集合的容器。今天我们要讨论另一种容器——用于存放固定:或密封接口。让我们开始吧。

基本语法

要声明一个密封类或接口,我们只需要在类或接口前面加上 sealed 修饰符 即可:

sealed class CustomError 
sealed interface CustomErrors

由于密封类和密封很相似(唯一的区别在于类和接口),我们将只继续使用密封类示例。

密封类是,因此不能被实例化。以下代码会报错:

fun main() {
    // Sealed types cannot be instantiated
    val customError = CustomError()
}

当然,你可以 延长 它。

与普通类一样,你可以声明构造函数,但密封类中的构造函数必须是私有的或 受保护的 :

sealed class CustomError {

    constructor(type: String) {} // protected (default) 
    private constructor(type: String, code: Int) {} // private
    public constructor() {} //  Public gives error
}

你也可以像在任何普通类中一样使用 主构造函数 :

//primary constructor 
sealed class CustomError(type: String)

密封类与枚举

理解密封类的最佳方法之一是将其与枚举进行比较。简而言之,密封类类似于枚举,但更加灵活。这意味着什么呢?请看下面的例子:

enum class Staff(numberOfLessons: Int)  {
    TEACHER(2), MANAGER("Manager is managing")
}

枚举类型无法做到这一点,但密封类可以做到:

sealed class Staff {
    class Teacher(val numberOfLessons: Int) : Staff()
    class Manager(val Responsibility: String) : Staff()
    object Worker : Staff()
}

枚举常量只有一种类型,而密封类则提供了多个实例,具有更大的灵活性。我们可以得出结论:枚举用于表示一组固定的值,而密封类用于表示一组与其相同的固定子类。

枚举类型不能继承自类或接口,而密封类可以。请看下面的例子:

open class Person {
    fun whoAmI(name: String): String {
        return "I am $name"
    }
}

sealed class Staff : Person() {
    class Teacher(val numberOfLessons: Int) : Staff()
    class Manager(val Responsibility: String) : Staff()
    object Worker : Staff()
}

fun main() {
    val worker = Staff.Worker
    println(worker.whoAmI("Worker"))
}

我们声明了一个简单的 Person 类,它有一个,然后创建了一个密封类 Staff 来继承它,这赋予了我们之前讨论过的 继承 能力。在 main 方法中,我们调用了 worker 中 Person 的方法。就这样。

另一方面,如果我们尝试使用枚举类型来实现这一点,则会抛出错误。下面的代码无法正常工作:

enum class Staff : Person() {
    //...//
}

密封类和 when 表达式

密封类通常与 when 表达式一起使用,因为每个类都被视为一个 case。举个例子:

sealed class Staff {
    class Teacher(val numberOfLessons: Int) : Staff()
    class Manager(val Responsibility: String) : Staff()
    object Worker : Staff()
}

fun listTheTasks(staff: Staff) = when (staff) {
    is Staff.Teacher -> println("The teacher has ${staff.numberOfLessons} lessons today")
    is Staff.Manager -> println("The manager is doing ${staff.Responsibility} today")
    Staff.Worker -> println("Worker is fixing the projector for profs in CS, all respect to him.")
}

我们声明了一个名为 Staff 密封类,其中包含两个类和一个对象。在没有状态的情况下,对象更合适。然后我们创建了一个名为 listTheTasks 函数。请注意,当对象是类时,` is 必需的,而当是类时则不需要。由于我们已经处理了所有情况,所以没有 else 。

让我们运行一下这个函数:

fun main() {
    val teacher = Staff.Teacher(3)
    val worker = Staff.Worker
    listTheTasks(teacher)
    listTheTasks(worker)

}

// output:
// The teacher has 3 lessons today
// Worker is fixing the projector for profs in CS, all respect to him.

直接子类的位置

最后,还有一点需要注意。密封类和接口的直接子类必须声明在同一个包中。而间接子类则不受此限制。你可能会问,什么是直接子类和间接子类?下面我们用一个简单的例子来解释:

open class B : A() // class B is direct subclass of class A 
open class C : B() // class C is indirect subclass of class A and direct subclass of class B

如果父类和子类之间没有类,则为直接类。

官方 文档 中写道:“密封类的所有直接子类在编译时都是已知的。密封类所在的模块/包之外,不会出现其他子类。”

结论

在本主题中,我们了解到密封类,顾名思义,“密封”限制了类层次结构,这在我们想要表示一组固定的子类时非常有用。

我们还了解到,密封类类似于枚举,但更加灵活。此外,我们也了解了如何将其与 when 结合使用,最后,我们讨论了密封类中类层次结构的限制。现在,让我们来练习一下。

44 名学员喜欢这部分理论内容, 8 名学员不喜欢。 你呢?

报告拼写错误

相关条目

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注