Kotlin 特殊类型篇:密封类比枚举好使在哪?Nothing 到底是个啥?

Kotlin 特殊类型篇:密封类比枚举好使在哪?Nothing 到底是个啥?

这一篇聊几个 Kotlin 里比较特殊的类型,它们各有各的用途,理解透了能让代码更优雅。

1. 密封类:类型安全的「枚举升级版」

为什么需要密封类

枚举的局限:每个枚举常量只能有一个实例,无法携带不同数据。

kotlin 复制代码
// 枚举:所有实例都是单例,无法携带不同数据
enum class ExpressStatus {
    TO_BE_SHIPPED,
    IN_TRANSIT,  // ❌ 没法表示「在哪」
    SIGNED        // ❌ 没法表示「谁签收」
}

密封类的优势:子类可以是 data class,携带不同数据。

密封类基本用法

kotlin 复制代码
sealed class ExpressStatus

// 无数据的单例
object ToBeShipped : ExpressStatus()

// 带数据的数据类
data class InTransit(val location: String) : ExpressStatus()
data class Signed(val receiver: String, val time: String) : ExpressStatus()

配合 when 使用:编译器自动检查

这是密封类最大的价值------穷尽性检查

kotlin 复制代码
fun showStatus(status: ExpressStatus) {
    when (status) {
        ToBeShipped -> println("待发货")
        is InTransit -> println("运输中:${status.location}")
        is Signed -> println("签收人:${status.receiver}")
        // 编译器保证覆盖了所有情况,不用写 else
    }
}

关键:如果漏掉任何一个子类,编译器直接报错。

实战:网络请求状态(最常用场景)

kotlin 复制代码
sealed class Result<out T> {
    object Loading : Result<Nothing>()
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val message: String) : Result<Nothing>()
}

// ViewModel 中的用法
sealed class HomeUiState {
    object Loading : HomeUiState()
    data class Success(val banners: List<Banner>) : HomeUiState()
    data class Error(val msg: String) : HomeUiState()
}

2. 枚举类:有限的选项

基本用法

kotlin 复制代码
enum class Color {
    RED, GREEN, BLUE
}

// 带属性和方法
enum class Status(val desc: String) {
    SUCCESS("成功"),
    FAILURE("失败"),
    LOADING("加载中");
    
    fun isSuccess() = this == SUCCESS
}

枚举的实用方法

scss 复制代码
// 通过名字获取枚举值
val status = Status.valueOf("SUCCESS")

// 遍历所有枚举值
Status.values().forEach { println(it) }

// Kotlin 简化写法
enumValues<Status>().forEach { }

// 获取枚举名称和序号
status.name    // "SUCCESS"
status.ordinal // 0

3. Pair 和 Triple:临时数据容器

Pair:两个数据

arduino 复制代码
// 创建方式
val pair1 = Pair("苹果", 5.5)
val pair2 = "苹果" to 5.5  // 中缀写法,更简洁

// 访问
pair.first   // "苹果"
pair.second  // 5.5

// 解构
val (name, price) = pair

Triple:三个数据

kotlin 复制代码
// 创建
val triple = Triple("张三", 25, true)

// 访问
triple.first   // "张三"
triple.second  // 25
triple.third   // true

// 解构
val (name, age, isVip) = triple

使用场景

kotlin 复制代码
// 函数返回多个值
fun getUserInfo(): Pair<String, Int> {
    return "张三" to 25
}

val (name, age) = getUserInfo()

⚠️ 注意:优先用命名数据类

Pair/Triple 属性名 first/second/third 没有业务含义,代码可读性差:

kotlin 复制代码
// ❌ 不推荐:可读性差
fun login(): Triple<String, Long, Boolean> {
    return Triple("token", 1001L, true)
}
val (token, userId, isVip) = login()

// ✅ 推荐:命名数据类
data class LoginResult(
    val token: String,
    val userId: Long,
    val isVip: Boolean
)
fun login(): LoginResult = LoginResult("token", 1001L, true)
val result = login()

什么时候用 Pair/Triple

  • 临时、局部使用,不对外暴露
  • 数据简单,没有明确业务绑定
  • 快速原型开发

4. Nothing:永远不返回的类型

是什么

Nothing 是 Kotlin 的特殊类型,表示「永远不会有返回值」。

典型场景

kotlin 复制代码
// 抛异常的函数
fun throwError(): Nothing {
    throw IllegalStateException("错误")
}

// TODO 函数的返回类型
val name: String = TODO("稍后实现")

Nothing 的特点

kotlin 复制代码
// 1. 没有实例
// val nothing = Nothing()  ❌ 编译报错

// 2. 是所有类型的子类型
val str: String = TODO()  // 可以,因为 Nothing 是 String 的子类型
val int: Int = TODO()     // 也可以

5. 内联类:类型别名增强版

kotlin 复制代码
// 类型别名:编译后是 String
typealias UserId = String

// 内联类:有类型安全,编译后是 String
@JvmInline
value class UserId(val value: String)

内联类的优势:类型安全 + 无性能损耗。

less 复制代码
@JvmInline
value class UserId(val value: String)
@JvmInline
value class OrderId(val value: String)

fun getUser(id: UserId) { }
fun getOrder(id: OrderId) { }

// 编译器会检查类型
getUser(UserId("123"))    // ✅
getUser(OrderId("123"))   // ❌ 编译报错,类型不匹配

常见问题

Q:密封类和枚举怎么选?

A:

  • 选项有限、固定,且不需要携带不同数据 → 枚举
  • 选项有限,但不同选项需要携带不同数据 → 密封类
  • 需要穷尽性检查 → 密封类

Q:密封类可以是 data class 吗?

A:可以。密封类的子类可以是 data class,享受数据类的所有特性(equals、hashCode、toString、copy)。

总结

表格

类型 用途 特点
密封类 表示有限的、固定的几种类型 子类可带数据,配合 when 穷尽检查
枚举 表示有限的、固定的几选项 所有实例是单例
Pair/Triple 临时承载 2-3 个数据 属性无业务含义,临时场景用
Nothing 表示永远不返回 抛异常、TODO
内联类 类型安全的类型别名 有类型检查,无性能损耗
相关推荐
沅霖4 小时前
Android Studio Java工程开发环境,怎么切换到Kotlin开发环境
android·kotlin·android studio
Kapaseker4 小时前
Kotlin SharedFlow 的三个参数到底有啥用
android·kotlin
阿巴斯甜5 小时前
by 和by lazy 懒加载
kotlin
三少爷的鞋7 小时前
Android 架构系列之MVVM 和 MVI 算架构吗?
android·kotlin
只可远观1 天前
Android 自动埋点(页面打开 / 关闭 + 点击事件)完整方案
android·kotlin
aqi001 天前
FFmpeg开发笔记(一百零二)国产的音视频移动开源工具FFmpegAndroid
android·ffmpeg·kotlin·音视频·直播·流媒体
阿巴斯甜1 天前
子协程的异常传播(CoroutineExceptionHandler ):
kotlin
alexhilton2 天前
Android上的ZeroMQ:用发布/订阅模式连接Linux服务
android·kotlin·android jetpack
Fate_I_C2 天前
View Binding的基础使用
android·kotlin·viewbinding