如何处理 Java Stream API 中的 Checked Exceptions?

Java 8 Stream API 方法(如 mapfilter 和 forEach)不允许直接检查异常。

在 Java Stream API 中处理异常可能很棘手,因为 mapfilter 和 forEach 等典型方法需要不允许检查异常的函数接口。我们不能处理这些,至少不能直接处理。

例如,下面的代码将导致我们的 Stream 管道中出现异常:

List<Integer> numbers = Arrays.asList(10, 5, 0, 3);

// This will throw ArithmeticException when number is 0
        numbers.stream()
                .map(number -> divide(100, number)) 
                .forEach(result -> System.out.println("Result: " + result));

这些是处理 Checked Exceptions (受检异常)的非常标准的方法,所以让我们看看实现这一点的方法:

将选中的异常包装在 Unchecked Exceptions (非受检异常)中

public class StreamExceptionHandling {
    public static void main(String[] args) {
        List<String> data = Arrays.asList("1", "2", "three", "4");
        List<Integer> result = data.stream()
            .map(s -> wrap(() -> Integer.parseInt(s)))
            .collect(Collectors.toList());
        System.out.println(result);
    }
    @FunctionalInterface
    public interface CheckedFunction<T, R> {
        R apply(T t) throws Exception;
    }
    public static <T, R> java.util.function.Function<T, R> wrap(CheckedFunction<T, R> function) {
        return t -> {
            try {
                return function.apply(t);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

处理 Lambda 中的异常

直接在 lambda 表达式中捕获异常,但这可能会使代码不那么干净。

List<String> data = Arrays.asList("1", "2", "three", "4");

List<Integer> result = data.stream()
    .map(s -> {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            System.err.println("Failed to parse: " + s);
            return null;
        }
    })
    .filter(Objects::nonNull)
    .collect(Collectors.toList());
System.out.println(result);

对函数使用自定义包装器

创建一个自定义包装器,用于处理异常并返回特殊结果或重新引发异常。

List<String> data = Arrays.asList("1", "2", "three", "4");

List<Optional<Integer>> result = data.stream()
    .map(s -> handleException(() -> Integer.parseInt(s)))
    .collect(Collectors.toList());
result.forEach(System.out::println);


public static <T> Optional<T> handleException(Supplier<T> supplier) {
    try {
        return Optional.ofNullable(supplier.get());
    } catch (Exception e) {
        return Optional.empty();
    }
}

使用自定义功能接口

创建允许引发异常并相应地处理异常的自定义函数接口。

@FunctionalInterface
public interface ThrowingFunction<T, R> {
    R apply(T t) throws Exception;
}

public static <T, R> Function<T, R> throwingFunctionWrapper(ThrowingFunction<T, R> function) {
    return i -> {
        try {
            return function.apply(i);
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    };
}
List<String> data = Arrays.asList("1", "2", "three", "4");
List<Integer> result = data.stream()
    .map(throwingFunctionWrapper(Integer::parseInt))
    .collect(Collectors.toList());
System.out.println(result);

## 使用 Try Wrapper(来自 Vavr 等库)

如果您愿意使用外部库,Vavr 提供了一个 Try monad,它允许以更实用的方式处理异常。

import io.vavr.control.Try;

List<String> data = Arrays.asList("1", "2", "three", "4");
List<Integer> result = data.stream()
    .map(s -> Try.of(() -> Integer.parseInt(s)).getOrElse(-1))
    .collect(Collectors.toList());
System.out.println(result);

感谢阅读,希望本文对您有用!

请登录后发表评论

    没有回复内容