我怎样才能 return 一个可选的而不是抛出异常?

How can I return an optional instead of throwing an exception?

我有一个可能会引发错误的方法。这个异常是我无法控制的,因为它是一个 IO 异常,而不是 I 抛出的异常。不是在我的方法中添加 throws 语句或将其放入 try 和 catch 块中,我是否能够通过此方法 return 一个可选的?

总结:我有一个方法 return 一个值 T,除了要得到那个值,我必须在我的方法中添加一个 try/catch 块或抛出语句。我可以 return 一个 T 类型的 Optional 而不是抛出这个错误吗?

“而不是在我的方法中添加 throws 语句或将其放入 try and catch 块中”

好吧,这是你的两个选择。你可以

  • 自己捕获异常并 return 一个空的 Optional。这不是一个好主意,因为它隐藏了异常,OR
  • 您可以让异常传播。
  • 我想第三种选择是将异常包装在未经检查的异常中,但这只会让您的界面更加模糊。

如果你真的想将错误处理推迟到调用者而不是 throw,理想的解决方案是实现一个复合对象到 return,包含错误状态和 return 值,如果有的话。

您可以使用这样的代码来包装 return 具有捕获异常的值和 return 空可选值的代码:

import java.util.Optional;
import java.util.function.Supplier;

@FunctionalInterface
public interface ThrowingSupplier<T> extends Supplier<Optional<T>> {
    @Override
    default Optional<T> get() {
        return getCapturingExceptions(this);
    }

    T getOrThrow() throws Exception;

    static <U> Optional<U> getCapturingExceptions(ThrowingSupplier<U> supplier) {
        try {
            return Optional.ofNullable(supplier.getOrThrow());
        } catch (Exception e) {
            e.printStackTrace();
            return Optional.empty();
        }
    }
}

使用示例:

import org.junit.Test;

import java.util.Optional;

import static org.junit.Assert.assertEquals;

public class ThrowingSupplierTest {
    @Test
    public void testSuccessfulSupplier() {
        assertEquals(
                Optional.of("Successful"),
                ThrowingSupplier.getCapturingExceptions(() -> "Successful")
        );
    }

    @Test
    public void testThrowingSupplier() {
        assertEquals(
                Optional.empty(),
                ThrowingSupplier.getCapturingExceptions(() -> {
                    throw new Exception("Expected failure; will be logged but test should pass");
                })
        );
    }
}

这具有 Jim Garrison 的回答中概述的所有缺点,我同意他的观点,即允许异常传播通常更好(必要时包装在未经检查的异常中)。尽管如此,这有时还是很有用的。例如,如果您需要实现一个不允许检查异常的接口,并且允许未经检查的异常向上传播调用堆栈会产生不良影响,例如杀死未正确处理它们的线程。为此,此示例实现了 Supplier(不能抛出未经检查的异常)并且可以在任何需要该接口的地方使用。

您可能想要进行的一些更改:

  1. 此记录使用 e.printStackTrace() 捕获的异常。重要的是不要完全丢失异常消息和堆栈跟踪,但如果您的应用程序使用日志记录框架,您可能更愿意使用它记录异常。

  2. 它使用 Optional.ofNullable 假设包装代码可能 return null,这在 Optional 中是不允许的。这意味着它可能 return 一个空的 Optional 即使没有发生异常。如果原码不希望returnnull,可以改成Optional.of。但是,如果包装代码在那种情况下执行 return nullOptional.of 将抛出一个 NullPointerException,它将被 catch 块捕获并导致一个空 Optional return 无论如何。

还应注意,如果包装代码 return 本身是 Optional 类型,这将被包装在另一个 Optional 层中。获取底层价值需要解包两次。