什么是密封类
密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。
以上是官网介绍;一脸蒙逼.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
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入门潜修之类和对象篇—密封类及其原理