opoojkk

Kotlin的枚举、密封类和密封接口

lxx
目次

枚举 #

下面涉及到的语言均是Kotlin,理论上Kotlin和Java在枚举中具有的功能是相同的。

说到枚举,就有先入为主的概念,认为枚举只能定义两个简单的状态,像是下面这样:

1
2
3
enum class State{
  OPEN, CLOSE;
}

其实不是这样的,这只是最最基本的用法,把枚举当做常量使用了。

变量 #

枚举中是可以定义变量的。

1
2
3
4
5
enum class State(val value:Int){
  Success(0), Fail(1);
}

printf(State.Success.value)

在Kotlin语言中,如果构造方法中出现val了,这个类就会增加一个变量。枚举也是一样的,上面就为枚举类增加了一个value的变量,而且是val的,不可以修改值的。

既然可以是val,是不是也可以是var呢,是可以的。

1
2
3
4
5
fun main() {
    println(State.Success.value)
    State.Success.value = 2
    println(State.Success.value)
}

打印的结果如下:

1
2
0
2

方法 #

枚举中不只可以有变量,同样也可以有方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
enum class State(var value: Int) {
    Success(0), Failed(1);

    fun greeting() {
        println("hello, this is $value")
    }
}

fun main() {
    State.Success.greeting()
    State.Fail.greeting()
}

打印出结果:

1
2
hello, this is 0
hello, this is 1

可能已经开始混乱了,这和我用的枚举不一样啊。是不一样,枚举不只是一个常量,它也是个类。

不妨转换一下思路,枚举(State)首先是一个类,枚举中的每个常量(Success, Fail)也是一个类型,是只有一个实例的类型,而这个实例就是它本身。为了要保证每一个类型都只被创建一次,构造方法是被限制的,没有办法创建出相同类型的第二个实例,还是那上面的例子,Success就是一种类型,这个类型的实例是Success,也只有这么一个实例。

类型会有方法的重写。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
enum class State(var value: Int) {
    Success(0){
        override fun greeting() {
            println("success never greeting")
        }
    }, Fail(1);

    open fun greeting() {
        println("hello, this is $value")
    }
}

输出就变成了

1
2
success never greeting
hello, this is 1

⚠️枚举类中的常量是需要首先声明的。也就是要先声明Success和Fail,然后才可以是声明greeting方法。

枚举类适用的场景就是根据它有限个常量的特性,常用到的像是标识状态,也可以用作实现状态模式(前面提到可以重写方法)。

密封类 #

密封类开始是Kotlin的概念,Java也在JDK17中加入。

密封类和平时用到的类差别在继承上。密封类是允许继承的,只允许文件内的类继承。也因此能够保证子类个数是有限的,只有文件内的那些。密封类更像是一个抽象类,是不能在外部直接使用定义了的密封类的,一定要访问内部的子类。

之类的类型可以是abstract class, open class, class, object几种类型。密封类中的子类同样可以实现其他的接口,这和其他类型的子类没有什么差异。

对应的使用场景和枚举类似,涉及到状态的,可以考虑用它。

为什么用它不是用枚举呢,它可以比枚举更灵活,密封类的子类可以是抽象类,或者open class,这些类型的子类又可以继续继承。

1
2
3
4
5
6
7
sealed class State {
    open class Success : State()
    abstract class Error : State()7
    class NetError : Error()
    class ServerError : Error()
    object Loading : State()
}

密封接口 #

密封接口又有什么用呢?

密封和接口看起来好像是两个没有什么关联的呀,密封保证封闭,接口是开放的。

理解密封接口绕个弯可能会更容易些,密封类中像是上面的例子,State类明明没有任何变量和方法,很像是接口,这么看果然顺了很多。

密封接口为什么设计成允许外部继承的,是我不理解的。Kotlin中的类默认是final不允许继承的,使用密封接口强制不允许继承吗,我只想到这一种可能。如果不是这样,那和普通的接口又有什么差别呢,难道是给Java用的?Kotlin的协程已经不可以在Java中使用了,而且Java中密封接口的实现类是可以继续继承的。

标签:
Categories:

评论

评论由 giscus 提供;如果未加载,可能是网络环境阻止了 giscus。