我怎样才能 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
(不能抛出未经检查的异常)并且可以在任何需要该接口的地方使用。
您可能想要进行的一些更改:
此记录使用 e.printStackTrace()
捕获的异常。重要的是不要完全丢失异常消息和堆栈跟踪,但如果您的应用程序使用日志记录框架,您可能更愿意使用它记录异常。
它使用 Optional.ofNullable
假设包装代码可能 return null
,这在 Optional
中是不允许的。这意味着它可能 return 一个空的 Optional
即使没有发生异常。如果原码不希望returnnull
,可以改成Optional.of
。但是,如果包装代码在那种情况下执行 return null
,Optional.of
将抛出一个 NullPointerException
,它将被 catch
块捕获并导致一个空 Optional
return 无论如何。
还应注意,如果包装代码 return 本身是 Optional
类型,这将被包装在另一个 Optional
层中。获取底层价值需要解包两次。
我有一个可能会引发错误的方法。这个异常是我无法控制的,因为它是一个 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
(不能抛出未经检查的异常)并且可以在任何需要该接口的地方使用。
您可能想要进行的一些更改:
此记录使用
e.printStackTrace()
捕获的异常。重要的是不要完全丢失异常消息和堆栈跟踪,但如果您的应用程序使用日志记录框架,您可能更愿意使用它记录异常。它使用
Optional.ofNullable
假设包装代码可能 returnnull
,这在Optional
中是不允许的。这意味着它可能 return 一个空的Optional
即使没有发生异常。如果原码不希望returnnull
,可以改成Optional.of
。但是,如果包装代码在那种情况下执行 returnnull
,Optional.of
将抛出一个NullPointerException
,它将被catch
块捕获并导致一个空Optional
return 无论如何。
还应注意,如果包装代码 return 本身是 Optional
类型,这将被包装在另一个 Optional
层中。获取底层价值需要解包两次。