Android kotlin之数据类和密封类(3)

Kotlin数据类与密封类详解

1.数据类

经常创建一些只保存数据的类。 在这些类中,一些标准函数往往是从数据机械推导而来的。在 Kotlin 中,这叫做 数据类 并标记为 data

data class Person(val name: String, val age: Int)

复制

在很多情况下,我们需要复制一个对象改变它的一些属性,但其余部分保持不变。 copy() 函数就是为此而生成。对于上文的 User 类,其实现会类似下面这样:

data class User(val name: String , val age: Int )

val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)

解构声明

有时把一个对象 解构 成很多变量会很方便,例如:

val (name, age) = jane  //结构声明

一个解构声明会被编译成以下代码:

var name=jane.component1()
var age=jane.component2()

任何表达式都可以出现在解构声明的右侧,只要可以对它调用所需数量的 component 函数即可。 当然,可以有 component3() 和 component4() 等等

2.密封类

密封类来表示受限的类继承结构:当一个值为有限几种的类型, 而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合 也是受限的,但每个枚举常量只存在一个实例,而密封类 的一个子类可以有可包含状态的多个实例。

声明一个密封类,使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。

sealed 不能修饰 interface ,abstract class(会报 warning,但是不会出现编译错误)

    sealed class Expr{ //密封类  默认private

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

    fun eval(expr: Expr): Double = when (expr) {
        is Expr.Const -> expr.number
        is Expr.Sum -> eval(expr.e1) + eval(expr.e2)
        Expr.NotANumber -> Double.NaN
    }

一个密封类是自身抽象的,它不能直接实例化并可以有抽象(abstract)成员。

密封类不允许有非-private 构造函数(其构造函数默认为 private

 

<think> 我们正在讨论Kotlin密封类。根据引用[1]引用[2],密封类Kotlin中用于实现受限的类层次结构,特别适合表示有限的状态或类型。密封类的所有子类必须在同一文件中声明(在Kotlin 1.1之后,允许在同一个文件的任何地方声明)。密封类本身是抽象的,不能直接实例化。 引用[1]提到:在密封类中使用when表达式时,需要注意else子句的使用。实际上,当when表达式覆盖了密封类的所有可能子类时,不需要else分支,这提供了编译时的穷尽性检查,避免遗漏情况。 引用[2]强调:密封类提供类型安全when的穷尽性检查,适合有限状态建模。 引用[3]则从数据类的角度引入,但密封类数据类不同,密封类用于表示封闭的类层次结构。 现在,我将详细解析密封类的语法用法。 1. 定义密封类: 使用`sealed`关键字修饰类。例如: ```kotlin sealed class Result ``` 2. 定义子类: 密封类的子类可以是任何类:数据类、对象、普通类等,但必须在同一文件中(在Kotlin1.1之后,允许在同一个文件中的任何位置,但必须在同一个文件中)。例如: ```kotlin data class Success(val data: String) : Result() data class Error(val message: String, val code: Int) : Result() object Loading : Result() ``` 3. 使用密封类的优点: - 类型安全:因为密封类的子类是有限的,所以当我们使用when表达式时,编译器可以检查是否覆盖了所有情况。 - 省去else分支:在when表达式中,如果已经列出了所有子类,则不需要else分支。 4. 示例:使用when表达式处理密封类 ```kotlin fun handleResult(result: Result) = when (result) { is Success -> println("Success: ${result.data}") is Error -> println("Error: ${result.message}, code: ${result.code}") Loading -> println("Loading...") // 不需要else分支,因为所有情况都已覆盖 } ``` 如果漏掉某个子类,编译器会报错,这保证了代码的安全性。 5. 密封类与枚举类的区别: 枚举类中的每个枚举常量都是相同的类型,且只能有一个实例。而密封类的子类可以有多个实例,并且每个子类可以有自己的状态(即可以包含不同的属性方法)。 6. 密封类的继承: 密封类本身是抽象的,可以有抽象成员,子类必须实现这些抽象成员。 7. 密封类的使用场景: - 状态管理:如网络请求结果(Success, Error, Loading)。 - 表达式结果:如解析结果(Number, Sum, Difference)。 - 事件处理:用户操作事件(Click, Swipe, LongPress)等。 8. 注意事项: - 密封类的构造函数默认是protected的(在Kotlin 1.1之前是private的),因此不能在类外部继承密封类(只能在同一个文件中声明子类)。 - 在Kotlin 1.5之后,密封类的子类可以定义在不同的文件中,但要求密封类及其子类都在同一个模块中,并且使用同一个包名(需要显式指定包名)。不过,为了保持清晰,通常建议放在同一个文件中。 引用[1]中提到“函数作为参数”是函数的高级特性,但这与密封类没有直接关系,因此我们聚焦于密封类本身。 总结:Kotlin密封类通过限制子类的范围,提供了更强的类型安全性编译时检查,是处理有限集合类继承的理想选择。 下面是两个完整的示例: 示例1:定义密封类Result及其子类 ```kotlin sealed class Result data class Success(val data: String) : Result() data class Error(val message: String, val code: Int) : Result() object Loading : Result() ``` 示例2:使用when表达式处理Result ```kotlin fun process(result: Result) = when (result) { is Success -> { /* 处理成功 */ } is Error -> { /* 处理错误 */ } Loading -> { /* 处理加载中 */ } } ``` 这样,我们就详细解析了Kotlin密封类的语法用法。 根据要求,生成相关问题: §§相关问题§§ 1. Kotlin密封类在when表达式中为什么不需要else分支? 2. Kotlin密封类与枚举类有何区别? 3. Kotlin密封类的子类可以定义在同一个文件的哪些位置? 4. 如何在Kotlin密封类中定义抽象成员? 5. 密封类Android开发中有哪些典型应用场景? </think> ### Kotlin 密封类的语法与用法详解 #### 📌 一、基本语法 密封类使用 `sealed` 关键字声明,用于限制类的继承结构: ```kotlin sealed class Result { data class Success(val data: String) : Result() data class Error(val code: Int, val message: String) : Result() object Loading : Result() // 单例子类 } ``` **关键特性**: 1. **有限子类**:所有子类必须在同一文件声明(Kotlin 1.5+ 允许同模块不同文件) 2. **自动抽象**:密封类隐含 `abstract`,不可直接实例化 3. **编译时检查**:配合 `when` 表达式实现穷尽性检查[^1][^2] --- #### 🧩 二、核心用法 ##### 1. 状态建模(如网络请求) ```kotlin fun handleResult(result: Result) = when(result) { is Result.Success -> println("Data: ${result.data}") is Result.Error -> println("Error ${result.code}: ${result.message}") Result.Loading -> print("Loading...") // 无需 else,编译器确保覆盖所有情况 } ``` *✅ 优势*:避免无效状态,编译时强制处理所有分支[^1] ##### 2. 表达式树(如计算器) ```kotlin sealed class Expr { data class Num(val value: Int) : Expr() data class Sum(val left: Expr, val right: Expr) : Expr() } fun eval(expr: Expr): Int = when(expr) { is Expr.Num -> expr.value is Expr.Sum -> eval(expr.left) + eval(expr.right) } ``` ##### 3. 事件处理(UI交互) ```kotlin sealed class ViewEvent { data class Click(val x: Int, val y: Int) : ViewEvent() data class Swipe(val direction: String) : ViewEvent() object LongPress : ViewEvent() } ``` --- #### ⚠️ 三、关键注意事项 1. **`when` 表达式规则**: - 必须覆盖所有子类(否则编译报错) - 禁止使用 `else` 分支(否则失去穷尽性检查)[^1] ```kotlin // 错误示例:添加 else 会跳过编译检查 when(result) { ... else -> /* 应避免 */ } ``` 2. **子类限制**: - 子类可为 `data class`/`object`/普通类 - Kotlin 1.5+ 支持子类在同一模块的不同文件(需显式声明包名) 3. **与数据类的区别**: | 特性 | 数据类(`data class`) | 密封类(`sealed class`) | |--------------|----------------------------|--------------------------| | 核心用途 | 数据容器 | 类型安全的有限状态集 | | 自动生成方法 | `toString()`/`copy()` 等 | 无 | | 继承限制 | 可多地点继承 | 仅允许密封类文件内继承 | --- #### 💡 四、实际应用场景 1. **API响应封装**(成功/失败/加载) 2. **UI状态管理**(显示/隐藏/错误) 3. **有限状态机**(如订单状态:创建/支付/完成) 4. **解析结果**(有效数据/无效格式/网络错误)[^3] > 在 Android Jetpack Compose 中,密封类广泛用于管理界面状态: > ```kotlin > sealed class LoginState { > object Idle : LoginState() > object Loading : LoginState() > data class Success(val user: User) : LoginState() > data class Error(val exception: Throwable) : LoginState() > } > ``` --- #### 🔍 五、版本演进 - **Kotlin 1.0**:子类必须嵌套在密封类内部 - **Kotlin 1.1**:允许同文件任意位置声明子类 - **Kotlin 1.5**:支持同模块不同文件的子类(需相同包名)[^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值