我正在使用 Java 中的 Lombok 库,发现了一个名为 @SneakyThrows 的注释。 正如文档所述:
@SneakyThrows 伪造了编译器。换句话说,Lombok 不会包装或替换抛出的已检查异常,而是让编译器认为它是未检查异常。
换句话说,这是一种在编译时绕过异常的方法。但在我看来,这不应该是处理异常的正确方法,因为绕过的异常可能会在运行时表现出奇怪的行为。
那么@SneakyThrows应该在什么场景下使用呢?
添加到现有答案。我个人不喜欢检查异常。有关更多信息,请参阅:https://phauer.com/2015/checked-exceptions-are-evil/
雪上加霜的是,当避免检查异常时,代码会变得臃肿。考虑使用@SneakyThrows:
List<Instant> instantsSneaky = List.of("2020-09-28T12:30:08.797481Z")
.stream()
.map(Example::parseSneaky)
.collect(Collectors.toList());
@SneakyThrows
private static Instant parseSneaky(String queryValue) {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(queryValue).toInstant();
}
与非@SneakyThrows
private static Instant parseNonSneaky(String queryValue) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").parse(queryValue).toInstant();
}
List<Instant> instantsNonSneaky = List.of("2020-09-28T12:30:08.797481Z")
.stream()
.map(timeStamp -> {
try {
return parseNonSneaky(timeStamp);
} catch (ParseException e) {
throw new RuntimeException(e);
}
})
.collect(Collectors.toList());
因此,@SneakyThrows 的应用可以使代码更加简洁。
我相信这里的目的是使编译器不需要抛出任何异常添加到方法声明中。
例如,如果方法是
public void throwsCheckedException() {
throw new IOException("IO exception thrown");
}
这会导致编译时异常需要
public void throwsCheckedException() throws IOException {
throw new IOException("IO exception thrown");
}
注释 @SneakThrows 缓解了这个问题 - 原始方法声明为
@SneakyThrows
public void throwsCheckedException() {
throw new IOException("IO exception thrown");
}
这不会导致编译时错误。 注意 IDE 可能仍会将其突出显示为错误,例如在 IntelliJ 中,您将需要使用 Lombok 插件。
我认为文档对此非常清楚:
当您想要选择退出已检查异常时的常见用例 机制围绕两种情况:
不必要的严格接口,例如 Runnable - 无论是否从 run() 方法传播出异常,无论是否检查,它都会 被传递到线程的未处理异常处理程序。抓住一个 检查异常并将其包装在某种 RuntimeException 中是 只会掩盖问题的真正原因。
“不可能”的例外。例如,new String(someByteArray, "UTF-8");声明它可以抛出 UnsupportedEncodingException 但根据 JVM 规范,UTF-8 必须始终是 可用的。这里出现 UnsupportedEncodingException 的可能性大约为 当您使用 String 对象时出现 ClassNotFoundError,并且您没有捕获 那些也!
在 JAVA 8 及以上版本中使用 lambda 尤其是它不是一个简单的使用方法。
@SneakyThrows
在当前的传统应用程序开发中没有多大用处,可以在某些需要(尽管我没有这方面的专业知识)确定程序当前流程的状态的状态机程序中使用。这只是不同场景的 1 个示例,可能还有更多。
通常,当我们有一个接口并且接口中声明的方法有多个实现时,我们可能会发现很难处理实现中抛出的异常的签名更改。
假设我们有一个接口和 2 个实现。
public interface A {
void methodSignature(String x); // This has to be changed owing to the various Exception thrown from the implementations
}
public class B implements A {
@Override
void methodSignature(String x) throws IOException {
// implementation for B
}
}
public class C implements A {
@Override
void methodSignature(String x) throws ApiException {
// implementation of C
}
}
我们可以使用常见的 throws Exception 来代替 IOException 和 ApiException,但如果有更多类型的异常需要处理,可能会不清楚。
因此,我们使用 @Sneakythrows 并将异常发送到用 @ControllerAdvice 注释的 GlobalException 处理程序,以控制流程。
public interface A {
void methodSignature(String x);
}
public class B implements A {
@Override
@SneakyThrows
void methodSignature(String x) {
// implementation for B
}
}
public class C implements A {
@Override
@SneakyThrows
void methodSignature(String x) {
// implementation of C
}
}
当我们与界面用户签订最终合同时,这尤其有帮助。
全局异常处理程序的示例如下所示
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(
value = {HttpMessageNotReadableException.class, DataAccessException.class, Exception.class, RuntimeException.class})
public ResponseEntity defaultWebExceptionHandler(Exception ex, HttpServletRequest servletRequest, HttpServletResponse httpResponse) {
LOGGER.error("A controller threw an exception", ex);
ResponseEntity returnValue = null;
ErrorInfo errorInfo = new ErrorInfo();
try {
throw ex;
} catch (ResourceNotFoundException resourceNotFound) {
errorInfo.setUserMessage(resourceNotFound.getMessage());
errorInfo.setTechnicalDetails(resourceNotFound.getMessage());
errorInfo.setMessageCode(HttpStatus.NOT_FOUND.toString());
returnValue = new ResponseEntity(errorInfo, HttpStatus.NOT_FOUND);
} catch (HttpMessageNotReadableException hme) {
// ...
} catch (DataAccessException dae) {
// ...
} catch (Exception exception) {
// ...
}
configureHttpResponse(httpResponse, null, servletRequest.getRequestURI());
return returnValue;
}
}
我经常在以下场景中使用
@SneakyThrows
注释: