跳到主要内容

KT-7 枚举

分枝剪定

枚举(Enum),或者应该称作枚举类(Enum Class),是 Kotlin 中一种特殊的类,用来描述取值范围有限的对象(例如用户类型、星期数等)。它与类的大多数功能都相同,能用来批量创建对象,拥有属性和方法,唯一的区别是枚举类的值只能从预先设置的值中选择一个

enum class UserRole(private val type: String) {
    // 描述可能的值
    ADMIN("admin"),
    USER("user"),
    HIM("him");

    fun isHIM() = type == "him"
}

val a = UserRole.ADMIN      // 可以通过 `类名.枚举值名` 直接访问事先定义的值
val b = UserRole.USER
val c = UserRole.HIM
val d = UserRole("hacker")  // 不行!UserRole 只有三种可能的值,不能有新的东西

UserRole 看上去和正常的类差不多,但是在属性和方法前多出了几行(ADMINUSERHIM)等,这些描述的是枚举的可能取值。也就是说,UserRole 的任何一个对象,要么是 ADMIN,要么是 USER,要么是 HIM,没有其它可能!

枚举类的对象必须事先在枚举类当中构造,并且一旦确定后,任何枚举类的对象都只能是它们之一,这正是枚举一词的含义:我已经列出所有可能了,请选择一个,没有其它选项!

这些事先定义的好的值,例如上面的 ADMIN,就称作枚举值(Enum Values),或者枚举选项(Enum Options),可以通过 类名.枚举值名 来访问。在枚举创建过后,不能通过它的构造函数新建对象,任何时候如果我们需要一个 UserRole 的对象,就必须使用 UserRole.枚举值名 选择一个(或者从其它方法那里获取,但最终还是要做选择题)。

似是而非

尽管 UserRole.HIM 这种用法看上去很像属性访问,但枚举值不是枚举类的属性,这是完全不同的另一种语法,用来访问枚举的值,只不过它和属性访问使用相同的 . 作为记号而已。

枚举类可以拥有构造函数,用来初始化枚举值对象,构造函数的参数紧跟在枚举值名称后方的 () 里,这里的用法就和直接调用构造函数差不多,不过省去了类名,而是直接使用 枚举值名(参数, ...) 的形式。如果不需要向构造函数提供任何参数,则 () 就可以省略。

枚举值必须放在 {}所有属性和方法之前(在构造函数中快捷定义的属性,例如上面的 type,不受这一限制)。在最后一个枚举值之后,如果没有额外定义方法或者属性,那么最后的 ; 是可以去掉的,但是如果在枚举值下方还想定义方法或者属性,那么 ; 就不能省略(这或许是 Kotlin 中为数不多不能省略的分号之一)。

想象的终结

由于枚举类的特殊性,枚举类不能继承其它类,而任何类也不能继承自枚举类,因为枚举说到底就是限定了对象取值的类,如果允许它被继承或者从其它类继承,就破坏了这种封闭性,枚举便没有了意义。

不过,枚举类还是可以实现接口的,可以用来描述枚举对象的特性:

interface Named {
    fun printName()
}

enum class UserRole(private val type: String) : Named {
    ADMIN("admin"),
    USER("user"),
    HIM("him");

    fun isHIM() = type == "him"

    override fun printName() {
        println(type)
    }
}

UserRole.ADMIN.printName()  // 输出 admin