Kotlin Sealed Class And Interface

1,028 阅读2分钟

Kotlin 中存在 sealed 关键字可以修饰 class 和 interface ,表示密封类和接口。主要是为了限制类的继承结构以达到对继承进行控制的目的。

**密封类的子类在编译时明确可知。在代码编译后,不会有其他的子类出现。**例如,其他模块无法实现你定义的密封类。因此,每一个密封类实例的类型是有限的类型集合。密封接口及其实现也是如此:一旦编译了具有密封接口的模块,就不会出现新的实现。

与枚举类的区别

在某种意义上,与枚举类类似:枚举类型的值的集合也会受到限制,但每个枚举常量仅作为单个实例存在,而密封类的子类可以有多个实例,每个实例都拥有自己的状态。

密封类本身是抽象的,不能直接实例化,可以有抽象成员。

子类的位置

密封类的直接子类和接口必须生命在同一个包名下,他们可能是顶级或者嵌套在密封类内部,可以是内部的 interfaceclassobject 声明。

子类的可见性不受限制,密封类的子类必须具有合适的命名,他们不能是本地对象或匿名对象。

枚举类不可以继承自密封类(和其他类一样),但它们可以实现密封接口。

1.4 版本 interface 不可用 sealed 关键字修饰,但 1.5 支持了密封接口。

Sealed Interface

有时,我们虽然对外暴露了 interface,但是并不希望外界去实现它。此时就可以通过 Sealed Interface 去实现。

**枚举类实现 Sealed Interface 的作用是突破单继承限制。**举例说明,下面是一个枚举类,和它的反编译后的代码:

enum class JvmLang {
    Java, Kotlin, Scala
}

// 反编译
public final class JvmLang extends Enum{
    private JvmLang(String s,int i){
        super(s,i);
    }
    public static final JvmLang Java;
    public static final JvmLang Kotlin;
    public static final JvmLang Scala;
    ...
    static{
        Java = new Action("Java",0);
        Kotlin = new Action("Kotlin",1);
        Scala = new Action("Scala",2);
    }
}

由于单继承的限制,枚举类无法继承 Enum 以外的其他类,但有时候我们又需要将其进行分类,例如分为 高级语言 和 机器语言:

sealed interface Language

enum class HighLevelLang : Language {
    Java, Kotlin, CPP
}

enum class MachineLang : Language {
    ARM, X86
}

object AssemblyLang : Language

通过密封类突破单类型的限制

我们都知道 Java 语言中,继承关系只能有一个父类。

sealed class JvmLang {
    object Java : JvmLang()
    object Kotlin : JvmLang()
    object Groovy : JvmLang()
}

sealed class CompiledLang {
    object Java : CompiledLang()
    object Kotlin : CompiledLang()
    object Groovy : CompiledLang()
    object Cpp : CompiledLang()

}
// JvmLang 实际上是编译语言的一种
sealed class JvmLang : CompiledLang

object Java : JvmLang()
object Kotlin : JvmLang()
object Groovy : JvmLang()

object Cpp : CompiledLang()

以上的例子说明了, Java 可以同时被视为 JvmLang 也可当成 CompiledLang 。 类似 Groovy 的解释性语言的特性。可以把子类视为一种偏向的父类型。