Java 8中的Stream
与Lambda表达式结合在一起,确实使得开发中的很多常见任务变得更简单。通过级联多个不同的操作符,如map
、filter
和reduce
等,可以在一行代码里面完成很多的任务。那种一行代码搞定一切的愉悦感,着实让人很满意。今天我们来说说Stream
中不那么愉悦的部分 - 异常处理。
如果你看一眼Stream
中包含的所有方法,你会发现,这些方法都没有声明抛出任何checked异常。再看一眼接口Function
、Consumer
和Supplier
,其中包含的方法也是不抛出checked异常的。关于checked和unchecked异常的讨论由来已久。目前的趋势是尽量使用unchecked异常,但是checked异常也有它的用武之地。当然,这个不是我们今天要讨论的重点。这些方法都不抛出checked异常,也就意味着那些抛出checked异常的方法不能直接使用。这就涉及到了我们今天要讨论的如何处理异常的问题。
为了避免枯燥的讨论,我们来看一个具体的例子。我们有一个接口UserService
,其中包含了方法load
来根据用户的ID返回对应的User
对象。现在我们有一个包含了多个用户ID的List<String
对象,我们需要返回与这些ID对应的List<User>
对象。在load
方法的实现中,可能出现各种错误。所以load
方法声明了会抛出UserLoadException
异常。
public interface UserService {
User load(String id) throws UserLoadException;
}
现在我们要实现的是加载多个用户的方法List<User> loadAllUsers(List<String> userIds)
。
最直接的实现方式userIds.stream().map(userService::load)
是行不通的,编译错误,因为没有处理UserLoadException
异常。在继续之前,我们首先要明确的是,处理异常的逻辑是什么。这个看似简单的问题,其实很多时候都被我们忽略了。而这个问题的答案来自于具体的业务逻辑。
如果该方法一共接收到了10个用户ID,其中的2个用户在加载时出现了异常,那我们需要做什么?
第一种做法,我们可以忽略出错的那2个用户,而只返回一个包含了8个User
对象的List
。这种做法对应的业务逻辑就是尽最大努力的完成任务。
我们只需要把用户ID的Stream
映射成Optional<User>
的Stream
,再进行过滤即可。
public List<User> loadAllUsersBestEffort(final List<String> userIds) {
return userIds.stream().<Optional<User>>map(id -> {
try {
return Optional.of(this.userService.load(id));
} catch (UserLoadException e) {
return Optional.empty();
}
}).filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
}
第二种做法,整个加载过程直接失败,抛出异常,不返回任何结果。这种做法对应的业务逻辑就是把整个加载过程看成一个整体,不允许部分成功。
我们只需要把抛出的checked异常封装成unchecked异常,再重新抛出即可。由该方法的调用者来负责处理异常。
public List<User> loadAllUsersFailFast(final List<String> userIds) {
return userIds.stream().map(id -> {
try {
return this.userService.load(id);
} catch (UserLoadException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
}
第三种做法与第一种做法类似,只不过会把加载时出现的异常也记录下来,并返回给调用者。这样提供给调用者给多的信息和更多的选择。调用者可以选择使用这部分成功的结果,也可以选择直接抛出所产生的异常。
在增加了异常信息之后,方法的返回类型变成了Tuple2<List<User>, Optional<UserLoadException>>
。基本的实现方式是把用户ID的Stream
映射成Tuple2<List<User>, Optional<UserLoadException>>
类型的Stream
之后,再使用reduce
操作合并成单一的对象。我们通过Throwable.addSuppressed()
方法来把多个异常对象进行合并。
public Tuple2<List<User>, Optional<UserLoadException>> loadAllUsersWithException(
final List<String> userIds) {
return userIds.stream().map(id -> {
try {
return Tuple
.of((List<User>) Lists.newArrayList(this.userService.load(id)),
Optional.<UserLoadException>empty());
} catch (UserLoadException e) {
return Tuple.of((List<User>) Lists.<User>newArrayList(), Optional.of(e));
}
}).reduce(Tuple.of(new ArrayList<>(), Optional.empty()), (r1, r2) -> {
r1._1.addAll(r2._1);
if (r2._2.isPresent()) {
if (r1._2.isPresent()) {
r1._2.get().addSuppressed(r2._2.get());
} else {
return Tuple.of(r1._1, r2._2);
}
}
return r1;
});
}
上面给出了在Stream
中进行异常处理的三种常见思路。可以根据业务逻辑的需要进行选择。
相关推荐
Java Streams 中的异常处理是Java 8 中 Stream API 的一个重要方面。Stream API 和 Lambda 是 Java 8 的重要特性,让我们可以使用更具功能性的语法风格。但是在编写代码时,一个更大的问题是如何处理 Lambda 中的已...
- 为了避免空指针异常,Java 8 引入了 `Optional` 类。`Optional` 代表一个值可能存在也可能不存在,强制开发者显式处理 null 值,提高代码的健壮性。 6. **接口的私有方法和静态方法**: - Java 8 允许在接口中...
首先,Java 8中的主要新特性之一是Lambda表达式。Lambda表达式简化了函数式编程,允许开发者以更简洁的方式编写匿名函数。例如,可以使用lambda表达式来创建Runnable、Comparator或Function等接口的实例,使得代码...
4. **日期和时间API**:Java 8彻底改革了日期和时间处理,引入了`java.time`包,包含`LocalDate`、`LocalTime`、`LocalDateTime`等类,取代了原来的`java.util.Date`和`java.util.Calendar`。这些新类提供了更加友好...
java异常处理机制,异常的概念,发生的原因,throwable,捕获异常的简单思维导图
流API是Java 8中另一个重要特性,它为处理集合提供了新的途径。流可以看作是从数据源(集合、数组等)生成的一系列元素序列,支持过滤、映射、聚合等操作。例如,`list.stream().filter(s -> s.length() > 3)....
2. **Stream API**:这是Java 8中最显著的新特性之一,提供了一种处理数据的新方式,如过滤、映射、减少等操作。Stream API允许开发者对集合进行并行和串行处理,极大地优化了大规模数据处理的性能。 3. **方法引用...
Stream API是Java 8引入的一种处理数据的新概念,它提供了一种声明式编程风格,使得对集合数据进行过滤、映射和规约等操作更为简洁。Stream API可以用于处理任何数据源,如集合、数组、I/O通道甚至数据库查询结果。...
在Java 8中,异常处理有了新的方式,特别是在使用流(Stream) API和lambda表达式时。传统的try-catch块在lambda表达式中显得不够优雅,因为它们会破坏代码的简洁性。本文将介绍如何在Java 8中更优雅地处理异常。 ...
以上只是Java 8中部分重要特性,实际的“Java+8实战”书籍可能会涵盖更多细节,如如何使用新的日期和时间API进行日期计算、如何利用流API进行复杂的数据处理,以及如何有效地使用lambda和函数式接口进行函数式编程。...
3. **健壮性与安全性**:Java在设计上注重安全性和错误处理,如强类型检查和异常处理机制,以确保程序的稳定性和安全性。 4. **多线程**:Java内建了多线程支持,开发者可以轻松地实现并发编程,从而提高程序的执行...
以上只是Java 8 API中的一部分核心特性,完整的文档涵盖了更多细节,包括反射、注解处理、类型推断等方面的增强。尽管中文版可能与英文版存在细微差异,但它仍然是学习和查阅Java 8 API的重要参考资料。建议开发者...
Java8中的一些例子 此存储库包含一些用于检查 Java 8 功能的 Java 应用程序示例 例子.java 使用流从对象列表创建 Map 和 Set 基于POJO创建json AccountParser.java 和 JSONParser.java 从 API 读取 json 从文件中...
下面我们将深入探讨Java 8 API中的核心变化和重要特性。 1. **Lambda表达式** Lambda表达式是Java 8最显著的特性之一,它允许以简洁的方式定义匿名函数。这种语法使得代码更加简洁,特别是处理函数式编程场景时,...
4. **Stream API**: Stream API提供了处理集合的新方式,支持并行操作和数据流处理,如过滤、映射、收集等操作。例如:`list.stream().filter(s -> s.startsWith("A")).forEach(System.out::println);` 5. **日期和...
在Windows系统中,Java8的安装通常涉及以下步骤: 1. **下载**:访问Oracle官方网站(https://www.oracle.com/java/technologies/javase-jdk8-downloads.html),找到对应操作系统的Java8 JDK下载链接。注意选择32...
3. **Stream API**:Java 8引入的Stream API提供了一种新的数据处理方式,支持链式操作和并行处理,使得对集合进行操作更为简洁高效。例如,可以使用`stream().filter().map().collect()`等方法进行数据流的转换和...
Stream API是Java 8的另一个重大改进,它为处理集合数据提供了一种声明式方式。`java.util.stream`包包含了一系列操作,如map、filter、reduce等,可以对集合进行高效的并行或串行处理。Stream API特别适合大数据量...
Java 8 API中文版是为Java开发者提供的重要参考资料,它包含了Java Development Kit (JDK) 8中的所有类、接口和方法的详细说明。这个帮助文档以CHM(Compiled HTML Help)格式呈现,通常在Windows操作系统环境下使用...