Kotlin 从学习到 Android 第十一章 枚举类、嵌套类 和 密封类

本文介绍了Kotlin中的枚举类、嵌套类及密封类等高级类特性的使用方法。包括枚举类的基本用法、初始化、匿名类、枚举常量的使用;嵌套类和内部类的区别;以及密封类的定义与使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、枚举类

枚举类最基本的用法是实现类型安全的枚举:

enum class Direction {
    NORTH, SOUTH, WEST, EAST
}

每一个枚举常量都是一个对象,枚举常量间以 “,” 分割。

初始化

因为每个 enum 都是 enum 类的一个实例,所以可以对它们进行初始化:

enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
}

匿名类

枚举常量还可以声明它们自己的匿名类:

enum class ProtocolState {
    WAITING {
        override fun signal() = TALKING
    },

    TALKING {
        override fun signal() = WAITING
    };// 枚举类中定义了成员(下面的 signal() 函数),所以要用分号分开

    abstract fun signal(): ProtocolState
}

枚举常量的匿名类不仅可以定义自己的函数,也可以覆写枚举类的函数。注意:如果枚举类定义了任何成员,你必须用分号来分隔枚举常量和成员。

枚举常量的使用

和 Java 类似,Kotlin 中的枚举类也有列出已定义的枚举常量方法和通过枚举常量的名称获取一个枚举常量的方法。例如(假定枚举类的名称为 EnumClass):

EnumClass.valueOf(value: String): EnumClass
EnumClass.values(): Array<EnumClass>

如果枚举常量匹配不到 valueOf() 方法参数的值,那么将会抛出一个 IllegalArgumentException 。

从 Kotlin 1.1 开始,可以使用 enumValues() 和 enumValueOf() 函数通过泛型的方式来访问枚举类中的枚举常量:

enum class RGB { RED, GREEN, BLUE }

inline fun <reified T : Enum<T>> printAllValues() {
    print(enumValues<T>().joinToString { it.name })
}

printAllValues<RGB>() // RED, GREEN, BLUE

每一个枚举常量都有 name 和 ordinal 属性,分别标示这个常量在枚举类中的名称和次序:

println(RGB.BLUE.name)          // BLUE
println(RGB.BLUE.ordinal)       // 2

枚举常量也实现了 Comparable 接口,它们的自然顺序就是在枚举类中定义的顺序。


二、嵌套类

类中可以嵌套其它的类:

class Outer {
    private val bar: Int = 1
    class Nested {
        fun foo() = 2
    }
}

val demo = Outer.Nested().foo() // == 2

内部类

一个被 inner 标记的内部类可以访问外部类的成员。内部类对外部类的对象有一个引用:

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

匿名内部类

匿名内部类是通过 对象表达式 进行实例化的:

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
        // ...
    }

    override fun mouseEntered(e: MouseEvent) {
        // ...
    }
})

如果这个匿名对象是 java 接口的实例化,并且这个接口只有一个抽象方法,那么你可以直接用 接口名lambda表达式 的方式来实例化:

val listener = ActionListener { println("clicked") }

三、密封类

密封类用于表示受限制的类层次结构,即一个值可以从一个有限的集合中拥有一个类型,但是不能有任何其他类型。从某种意义上说,它们是枚举类的扩展:枚举类型的值集也受到限制,但每个枚举常量仅作为单个实例存在,而密封类的子类可以包含多个实例并包含状态。
要声明一个密封类,你可以在类名之前添加 sealed 修饰符。一个密封类可以有子类,但是所有的类都必须在密封类本身所在的文件中声明。(在 Kotlin 1.1 之前,规则更加严格:类必须嵌套在密封类的声明中)。

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

上面的例子使用了 Kotlin 1.1 的另一个新特性: 数据类 扩展其他类的功能,包括密封类。

注意:那些继承了一个密封类的子类(间接继承)的类可以放在任何地方,不一定是在同一个文件中。

// file01.kt
fun main(args: Array<String>) {

    fun eval(expr: Expr): Double = when(expr) {
        is Const -> expr.number
        is Sum -> eval(expr.e1) + eval(expr.e2)
        is TestN -> 20e0
        NotANumber -> Double.NaN
        // 由于覆盖了所有的可能性,所以不需要 else 分支
    }

    print(eval(TestA()))                // 20.0
}
sealed class Expr
data class Const(open val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
open class TestN : Expr()

// file02.kt
class TestA() : TestN()

使用密封类的关键好处是当你在使用一个 when表达式 时,如果可以验证语句覆盖了所有的情况,那么就不需要向语句中添加 else 子句。

### Kotlin 密封类枚举类的区别 #### 定义与基本概念 在 Kotlin 中,密封类(Sealed Classes)枚举类(Enum Classes)都是用于表示有限集合的工具。然而,两者的设计目标适用场景存在显著差异。 - **枚举类**是一种特殊的类形式,它允许定义一组固定的常量值。这些常量值是该类的唯一实例[^2]。 - **密封类**则是一个抽象的概念容器,它可以拥有多个具体的子类,但这些子类必须在同一文件中声明。通过这种方式,密封类能够表达更复杂的层次结构并支持多种不同的实现方式[^1]。 --- #### 成员属性与行为能力 - 枚举类中的每一个成员本质上只是一个对象实例,并且无法携带额外的状态或复杂的行为逻辑。如果需要扩展其功能,则可以通过 `object` 的方式为其单独添加方法或者字段[^2]。 - 而对于密封类来说,它的各个具体子类不仅可以继承父类的功能,还可以进一步自定义自己的状态参数以及独特的方法实现。这种灵活性使得密封类更适合用来描述那些具有不同表现形态的对象群组[^2]。 ```kotlin // 枚举类示例 enum class Color { RED, GREEN, BLUE; fun description(): String = when (this) { RED -> "Red color" GREEN -> "Green color" BLUE -> "Blue color" } } // 密封类示例 sealed class Result { data class Success(val value: Int) : Result() object Failure : Result() override fun toString(): String = when (this) { is Success -> "Success with $value" is Failure -> "Operation failed" } } ``` --- #### 使用范围与典型应用场景 - 当只需要简单地列举一些选项而不涉及任何动态变化的数据时,应该优先考虑使用枚举类。比如颜色的选择、星期几等等这样的情况非常适合作为枚举项来管理。 - 如果面临的是更加灵活的需求——即不仅限于静态列表而且还需要根据不同条件做出相应调整的话,则应当选用密封类作为解决方案之一[^1]。例如解析网络请求的结果可能成功也可能失败;又或者是游戏开发过程中角色的动作类型既包含了基础动作也涵盖了高级技能释放等多种可能性等情形下都可以借助密封类构建清晰易懂的业务模型[^1]。 --- #### 总结比较表 | 特性/类别 | 枚举类 | 密封类 | |------------------|-------------------------------------|---------------------------------------| | 实例数量 | 只能有一个单一实例 | 子类可有多个独立实例 | | 数据承载能力 | 不具备数据存储 | 支持携带任意数量及种类的数据 | | 方法定义权限 | 所有成员共享同一套公共API | 各个子类均可自由定制专属操作接口 | | 文件位置约束 | 无特别限制 | 必须与其直接派生的所有子类位于同一个源码单元内 | --- ### 示例代码展示 以下是两个实际应用的例子分别展示了如何利用这两种机制解决问题: ```kotlin fun processColor(color: Color): Unit = println(color.description()) val colorsList = listOf(Color.RED, Color.GREEN, Color.BLUE) colorsList.forEach { c -> processColor(c) } ``` 上面这段程序片段演示了怎样基于预设好的几种色彩来进行统一处理流程调用。 相比之下下面这个例子则是关于采用密封类解决更为棘手状况下的策略模式设计思路: ```kotlin fun handleResult(result: Result): String = result.toString() val resultsSequence = sequenceOf(Result.Success(42), Result.Failure) resultsSequence.map(::handleResult).forEach(::println) ``` 在这里我们看到即使面对不确定性的输入也能依靠精心规划过的分类体系得到妥善处置结果反馈给用户端呈现出来。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值