深入学习 Kotlin 特色之 Sealed Class 和 Interface

本文从特点、场景和原理等角度综合分析了Kotlin语言的sealed class和sealed interface。sealed class可限制类的扩展,其子类有特定的定义规则;sealed interface和sealed class特点相近,还能帮助实现多继承和扩展性。文章还对比了它们与枚举、接口的区别。

🚨 前言

sealed class 以及 1.5 里新增的 sealed interface 可谓是 Kotlin 语言的一大特色,其在类型判断、扩展和实现的限制场景里非常好用。

本文将从特点、场景和原理等角度综合分析 sealed 语法。

  • Sealed Class
  • Sealed Interface
  • Sealed Class & Interface VS Enum
  • Sealed Class VS Interface

🏁 Sealed Class

sealed class,密封类。具备最重要的一个特点:

  • 其子类可以出现在定义 sealed class 的不同文件中,但不允许出现在与不同的 module 中,且需要保证 package 一致

这样既可以避免 sealed class 文件过于庞大,又可以确保第三方库无法扩展你定义的 sealed class,达到限制类的扩展目的。事实上在早期版本中,只允许在 sealed class 内部或定义的同文件内扩展子类,这些限制在 Kotlin 1.5 中被逐步放开。

如果在不同 module 或 package 中扩展子类的话,IDE 会显示如下的提示和编译错误:

Inheritor of sealed class or interface declared in package xxx but it must be in package xxx where base class is declared

sealed class 还具有如下特点或限制:

  1. sealed class 是抽象类,可以拥有抽象方法,无法直接实例化。否则,编译器将提示如下:

    Sealed types cannot be instantiated

  2. sealed class 的构造函数只能拥有两种可见性:默认情况下是 protected,还可以指定成 private,public 是不被允许的。

    Constructor must be private or protected in sealed class

  3. sealed class 子类可扩展局部以及匿名类以外的任意类型子类,包括普通 class、data classobject、sealed class 等,子类信息在编译期可知。

    假使匿名类扩展自 sealed class 的话,会弹出错误提示:

    This type is sealed, so it can be inherited by only its own nested classes or objects

  4. sealed class 的实例,可配合 when 表达式进行判断,当所有类型覆盖后可以省略 else 分支

    如果没有覆盖所有类型,也没有 else 统筹则会发生编译警告或错误

    1.7 以前:

    Non-exhaustive ‘when’ statements on sealed class/interface will be prohibited in 1.7.

    1.7 及以后:

    ‘when’ expression must be exhaustive, add …

当 sealed class 没有指定构造方法或定义任意属性的时候,建议子类定义成单例,因为即便实例化成多个实例,互相之间没有状态的区别:

‘sealed’ subclass has no state and no overridden ‘equals()’

下面结合代码看下 sealed class 的使用和原理:

示例代码:

// TestSealed.kt
sealed class GameAction(times: Int) {
   
   
    // Inner of Sealed Class
    object Start : GameAction(1)
    data class AutoTick(val time: Int) : GameAction(2)
    class Exit : GameAction(3)
}

除了在 sealed class 内嵌套子类外,还可以在外部扩展子类:

// TestSealed.kt
sealed class GameAction(times: Int) {
   
   
    ...
}

// Outer of Sealed Class
object Restart : GameAction(4)

除了可以在同文件下 sealed class 外扩展子类外,还可以在同包名不同文件下扩展。

// TestExtendedSealedClass.kt
// Outer of Sealed Class file
class TestExtendedSealedClass: GameAction(5)

对于不同类型的扩展子类,when 表达式的判断亦不同:

  • 判断 sealed class 内部子类类型自然需要指定父类前缀
  • object class 的话可以直接进行实例判断,也可以用 is 关键字判断类型匹配
  • 普通 class 类型的话则必须加上 is 关键字
  • 判断 sealed class 外部子类类型自然无需指定前缀
class TestSealed {
   
   
    fun test(gameAction: GameAction) {
   
   
        when (gameAction) {
   
   
            GameAction.Start -> {
   
   }
            // is GameAction.Start -> {}
            is GameAction.AutoTick -> {
   
   }
            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechMerger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值