either 的核心价值是让 lambda 在可能失败时不停止 stream 流程,显式区分 left(失败)和 right(成功),实现类型安全、可追踪、可恢复的错误处理。

Java 标准库没有 Either,但它的核心价值很明确:让 Lambda 表达式在可能失败的场景下不中断流处理,同时把“成功值”和“异常原因”都保留下来,避免吞异常、丢上下文或强行抛运行时异常。这不是绕开限制,而是升级异常建模方式。
为什么 Either 比 RuntimeException 包装更合适
用 RuntimeException 包装受检异常(比如 throw new RuntimeException(e))能过编译,但代价是丢失类型信息、破坏调用方对错误的区分能力。而 Either 把结果显式分为 Left(失败,含异常)和 Right(成功,含值),让每个元素的成败可追踪、可聚合、可恢复。
- 不打断 Stream 流程:map 中某个元素解析失败,不会导致整个 stream 终止,后续元素照常处理
-
失败可分类处理:比如
Left是IOException就重试,是NumberFormatException就跳过并记录告警 -
类型安全可推导:返回类型是
Either<ioexception string></ioexception>,调用方一眼知道什么错、什么对
手写轻量 Either + Lambda 适配器(零依赖)
不需要引入 Vavr 或 functional-java,自己定义一个不可变的 Either<l r></l> 和配套工具方法即可:
先定义结构:
public sealed interface Either<l r> {
record Left<l>(L left) implements Either<l> {}
record Right<r>(R right) implements Either, R> {}
static <l r> Either<l r> left(L l) { return new Left(l); }
static <l r> Either<l r> right(R r) { return new Right(r); }
boolean isRight();
boolean isLeft();
R getOrElse(R defaultValue);
L getLeftOrElse(L defaultValue);
}</l></l></l></l></r></l></l></l>
再写一个通用转换器,把会抛异常的 Lambda 转成返回 Either 的函数:
public static <t r e extends exception> Function<t either r>> toEither(
ThrowingFunction<t r e> f) {
return t -> {
try {
return Either.right(f.apply(t));
} catch (E e) {
return Either.left(e);
}
};
}</t></t></t>
使用示例(读取多个路径内容):
List<path> paths = List.of(Paths.get("a.txt"), Paths.get("b.txt"));
List<either string>> results = paths.stream()
.map(toEither(path -> Files.readString(path)))
.toList();
results.forEach(either -> {
if (either instanceof Either.Right> r) {
System.out.println("✅ OK: " + r.right());
} else if (either instanceof Either.Left> l) {
System.err.println("❌ Fail: " + l.left().getMessage());
}
});</either></path>
用 Vavr 的 Try 替代 Either(推荐用于新项目)
Vavr(原 Javaslang)的 Try 更贴近实际需求:它本质是 Either<throwable t></throwable> 的特化,且自带恢复操作链。它不依赖函数式接口改造,直接嵌入 Lambda 即可:
// Maven 引入: io.vavr:vavr:0.10.4
import io.vavr.control.Try;
List<string> urls = List.of("http://a", "http://b");
List<try>> tries = urls.stream()
.map(url -> Try.of(() -> new URL(url).getHost()))
.toList();
tries.forEach(t -> {
if (t.isSuccess()) {
System.out.println("✅ Host: " + t.get());
} else {
System.err.println("❌ " + t.getCause().getClass().getSimpleName());
}
});
// 还可链式恢复:.map(url -> Try.of(...).recover(Exception.class, "fallback"))
</try></string>
Try 的优势在于:构造即执行、失败自动捕获、支持 map/flatMap/recover,完全契合函数式风格,且无需自定义接口或转换器。
关键提醒:别把 Either 当异常兜底的遮羞布
Either 或 Try 不是用来掩盖本该提前预防的错误。它们适合以下场景:
- 外部依赖不稳定(HTTP 请求、文件读取、数据库查询)
- 数据格式不可控(用户输入 JSON、CSV 解析)
- 需要逐项统计成功率或构建错误报告
但如果是 new String(bytes, "UTF-8") 这种 JDK 保证支持的编码,UnsupportedEncodingException 理论上不可能发生,就该用 @SneakyThrows 或直接转 RuntimeException,而不是塞进 Either 增加冗余分支。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











