Either — 一种函数式类型,表示两个可能选项之一的值:左侧(Left)表示错误,右侧(Right)表示成功。与异常不同,Either 在类型层面显式处理错误,无需 try-catch 块。根据 Arrow, 2026 的数据,Either 被广泛用于 Kotlin 项目中对可能失败的操作进行组合,且没有副作用。
要点
Either — 是一种从函数式编程中借鉴的代数数据类型,表示两个可能类型中恰好一个的值。在错误处理的语境中,约定如下:左类型(Left)包含问题描述,右类型(Right)— 成功的结果。
Either 的概念源自 Haskell 语言和 Either a b 类型范畴,其中 a 是左值的类型,b — 右值的类型。在移动开发中,Either 因 Kotlin 的 Arrow 库和 Swift 中的函数式方法而广受欢迎。
Either 相对于异常的主要优势 — 没有隐藏的执行路径。返回 Either 的函数在其签名中显式声明可能以错误结束。编译器控制是否处理了两种选项。
与抛出异常不同,Either 保持 数据流的透明性。调用返回 Either 的函数不需要在调用代码侧使用 try-catch — pattern-matching 或 fold 就足够了。这在采用响应式方法的架构中尤其重要,其中每个数据源返回 Either,转换链通过 map 和 flatMap 构建。
Either 由两个子类型组成:Left 和 Right。一个 Either 实例在每一时刻只能是其中之一。Kotlin 中的典型签名是 Either<E, A>,其中 E 是错误类型,A — 成功值的类型。
Left 表示故障情况。与异常不同,Left 不会中断执行流 — 它只是返回一个需要处理的值。错误类型可以是任何类型:String、Int、自定义 sealed class 或领域模型。
Right 包含操作的正确结果。名称反映了 “正确性” — 按照约定,Right 意味着成功。正是在 Right 上应用 map 和 flatMap 转换,从而无需检查每一步即可构建计算链。
flatMap — Either 组合的主要机制。如果当前值是 Right,flatMap 应用传入的函数并返回新的 Either。如果是 Left — flatMap 跳过转换并将错误传递下去。这种行为称为 short-circuit evaluation。
除了 flatMap,Either 还支持 mapLeft(用于转换错误)、fold(用于在同一个地方处理两种选项)和 getOrElse(用于提取带有默认值的值)。这些函数涵盖了所有场景:从简单的提取到 Kotlin 协程或 Swift 的 Combine 中的异步调用复杂组合。
fun parseInt(input: String): Either<String, Int> =
input.toIntOrNull()?.let { Right(it) }
?: Left("无法转换:$input")
fun divide(a: Int, b: Int): Either<String, Int> =
if (b == 0) Left("除以零")
else Right(a / b)
val result = parseInt("10")
.flatMap { divide(it, 2) }
在 Kotlin 生态系统中,Either 在 Arrow 库中实现。Kotlin 标准库提供 Result,但 Either 提供更大的灵活性:任意的错误类型、通过 flatMap 组合以及对函数式模式的支持。
Arrow — 一个为 Kotlin 增加 Either、Option、Validated 等类型的函数式库。Arrow.Either — 具有两个继承者的 sealed class:ArrowCore.Left 和 ArrowCore.Right。该库还提供了方便工作的扩展:.getOrElse()、.fold()、.mapLeft()。
让我们考虑一个真实场景 — Android 应用中可能带有错误的网络请求:无网络、服务器错误、无效响应。Either 允许将所有选项合并为一个返回类型。
sealed class NetworkError {
data class NoConnection(val message: String): NetworkError()
data class ServerError(val code: Int): NetworkError()
data class ParseError(val detail: String): NetworkError()
}
suspend fun fetchUser(id: String): Either<NetworkError, User> {
return try {
val response = api.getUser(id)
if (response.isSuccessful) {
Right(response.body()!!)
} else {
Left(NetworkError.ServerError(response.code()))
}
} catch (e: IOException) {
Left(NetworkError.NoConnection(e.message ?: ""))
}
}
在 Swift 中,从 5.0 版本开始出现了内置的 Result 类型,它在概念上类似于 Either,但有局限性:错误必须符合 Error 协议,成功值必须是一种类型。Swift 中的 Either 通过具有两个泛型参数的 enum 来实现。
Result<Success, Failure> — Swift 的内置类型,其中 Failure: Error。Either 不对错误类型施加限制,从而允许存储 String、自定义结构甚至通过嵌套 enum 存储多种错误类型。
enum Either<E, A> {
case left(E)
case right(A)
func map<B>(_ transform: (A) -> B) -> Either<E, B> {
switch self {
case .left(let e): return .left(e)
case .right(let a): return .right(transform(a))
}
}
}
let result: Either<String, Int> = .right(42)
let mapped = result.map { $0 * 2 }
Either 适用于需要显式且类型安全地处理错误(无需异常)的场景。让我们看看 Kotlin 和 Swift 移动开发中的主要用例。
不要对没有副作用的简单操作使用 Either — 普通的返回值更可靠且更易读。当故障是异常情况而非预期场景时,Either 也是多余的。
Either 在使用 Kotlin 协程时也很有效。返回 Either 的函数可以在协程内部调用,通过 fold 或 mapLeft 处理错误,而无需阻塞线程。这在具有 MVVM 架构的 Android 应用中尤其有用,其中每个仓库返回 Either,ViewModel 将结果转换为 UiState。
初次接触 Either 的开发人员经常犯类似的错误。让我们看看最常见的错误及其避免方法。
常见问题
Optional 表示值的存在或缺失(Some/None),但不说明缺失的原因。Either 提供两个具体的类型 — 左用于错误,右用于成功,从而允许传递故障的上下文。
在 Java 中没有内置的 Either,但 Vavr 和 functionaljava 库提供了实现。在 Android 开发中,来自 Vavr 的 Either 是使用 lambda 的函数式风格的流行替代方案。
当选项超过两个或具有不同结构时,Kotlin 中的 Sealed class 更方便。对于二元结果(错误/成功),Either 更紧凑并提供现成的函数式组合器。
Either 是不可变的,默认是线程安全的。在 Kotlin 中与协程配合时,Either 完美结合:flatMap 在 coroutine scope 内工作,错误处理不需要锁定。
不。Either 适用于预期会失败的操作(网络、验证、业务逻辑)。对于没有副作用的简单 getter 和计算,普通类型更易读且不会增加不必要的复杂性。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。