Kotlin密封类

什么是密封类

密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。

以上是官网介绍;一脸蒙逼.jpg

举个栗子,动物有哺乳动物,爬行动物,鸟类,鱼类,等等;用动物分类写一个密封类:

密封类 动物 Animal

子类 鸟类 继承 动物 Bird:Animal()

子类 鱼类 继承 动物 Fish:Animal()

转换成代码:

sealed class Animal

class Bird(var name: String) : Animal()
class Fish(var name: String) : Animal()

密封类特性

  • 声明一个密封类在类名使用 sealed 修饰符
  • 所有子类都必须在与密封类自身相同的文件中声明(子类的扩展类不受此控制)
  • 一个密封类是自身抽象的,它不能直接实例化并可以有抽象(abstract)成员
  • 密封类不允许有非-private 构造函数(其构造函数默认为 private)

再次蒙逼.jpg
不急,查看kotlin字节码

怎么查看?AndroidStudio-->Tools-->Kotlin-->Show Kotlin ByteCode

Animal 字节码

public abstract class com/example/mykoltin/Animal {


  // access flags 0x2
  private ()V//私有构造方法
   ...

  @Lkotlin/Metadata;(mv={1, 1, 13}, bv={1, 0, 3}, ...
}

Animal类使用abstract修饰符声明;private ()V 构造函数私有,意味着不能直接创建实例;也不能被其它类继承,那么Bird是怎么继承Animal的呢?

Bird类

public final class com/example/mykoltin/Bird extends com/example/mykoltin/Animal  {
 
  // access flags 0x1
  public (Ljava/lang/String;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    ...
   L1
    LINENUMBER 9 L1
    ALOAD 0
    ACONST_NULL
    INVOKESPECIAL com/example/mykoltin/Animal. (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
    ALOAD 0
    ALOAD 1
    PUTFIELD com/example/mykoltin/Bird.name : Ljava/lang/String;
    RETURN
   L2
    ...

  
}

Bird类构造方法调用了Animal构造方法,等下Animal不是私有的构造方法吗?

INVOKESPECIAL com/example/mykoltin/Animal. (Lkotlin/jvm/internal/DefaultConstructorMarker;)V

Bird类继承Animal后; Animal字节码

public abstract class com/example/mykoltin/Animal {


  // access flags 0x2
  private ()V//私有构造方法
   ...

  // access flags 0x1001
  public synthetic (Lkotlin/jvm/internal/DefaultConstructorMarker;)V//公共构造方法
  ...

  @Lkotlin/Metadata;(mv={1, 1, 13}, bv={1, 0, 3}, ...
}

Animal多了一个public构造方法,这个方法里面调用了自身私有构造方法,所以Bird是调用了Animal public构造方法实现继承的,一切都水落石出了。

使用场景

fun main(args: Array) {
    val animalName = getAnimalName(Fish("鲨鱼"))
    print(animalName)
}
fun getAnimalName (animal:Animal):String =when(animal){
    is Bird -> "鸟类:"+animal.name
    is Fish -> "鱼类:"+animal.name
}

sealed class Animal

class Bird(var name: String) : Animal()
class Fish(var name: String) : Animal()

使用密封类的关键好处在于使用 when 表达式 的时候,如果能够验证语句覆盖了所有情况,就不需要为该语句再添加一个 else 子句了

小结

密封类与枚举类有什么区别?

密封类在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例

还是用上文中动物分类举例,动物分类有鱼类,鸟类...;鱼类又分为鲨鱼类等,鲨鱼类又分大白鲨,虎鲨,锤头鲨,长尾鲨...(ps非严谨分类勿较真);这种情况用枚举就不太合适,因为密封类它的子类实例是可数的,也就是包含了多个实例。

class Shark(name: String) : Fish(name)

参考&引用

kotlin文档-密封类

kotlin入门潜修之类和对象篇—密封类及其原理

你可能感兴趣的:(Kotlin密封类)