Lol*_*lly 3 java lombok spring-boot
我正在尝试在我的 Spring Boot 应用程序中使用 Lombok 库。我遇到了 @SneakyThrows 注释。但我并没有真正充分利用它。通常,如果可能出现异常,捕获/抛出它总是好的,并且调用者也可以处理并捕获或抛出它,这是一个很好的实践。但是使用@SneakyThrows我们就绕过了它。那么它的真正优势是什么?
我浏览了很多链接,但它们都只说明了行为,我不理解真正的用例。
这里的功能的作者 - 这个答案大部分是客观的,但是这一切都具有一定的主观性 - 在某些时候,任何语言功能在告诉人们“应该”如何编程方面都有点独裁。这是无法避免的。通常,对 SO 的意见是不可接受的,但考虑到这是关于 lombok 的,并且 [A] 我写了这个功能,并且 [B] 我仍然是 lombok 的核心提交者,我想我的意见就是所要求的答案,并且这不能演变成一场意见战(除了我的共同提交者罗尔,他基本上支持这些意见)。
通常,如果可能出现异常,捕获/抛出它总是好的
不正确。接住/扔掉它通常很好,但并非总是如此。
检查异常的概念完全是凭空javac想象的。无法编译的原因:
public void foo() {
throw new IOException();
}
Run Code Online (Sandbox Code Playgroud)
是因为那里javac有一个if构造说:我不会编译这个。如果您破解javac并删除该构造,则生成的类文件完全没问题。类验证器(JVM 中的代码,用于检查执行类文件中的代码是否会导致问题)并不关心。JVM 也不关心。它将尽职尽责地运行该代码,然后...抛出异常。“但是……它没有声明!”,是的,JVM 不在乎。它不知道什么throws IOException意思。就目前而言,这是一条评论java.exe。这解释了为什么像 kotlin 这样的目标是在 JVM 上运行的语言可以做它们所做的事情(它们没有检查异常 - 你可以抛出任何你喜欢的东西,不管子句throws。包括 java 编写的检查异常)。
@SneakyThrows就像这样:它允许您抛出异常,而无需声明您这样做。异常不会被抑制、包装、忽略或以任何方式修改。@SneakyThrows只做一项工作:告诉编译器停止出错并继续执行。
捕获已检查异常或将其添加到您的throws行中是不正确的具体两种情况:
声明了异常,因此,您必须编写处理它的代码(要么将catch其处理,要么将其粘贴在throws方法的子句中),但是,通过规范或可能只是简单的经验和代码审查,您可以100%确定异常不可能发生。
这是一个简单的例子:
new String(someByteArray, "UTF-8");
Run Code Online (Sandbox Code Playgroud)
此代码通过将提供的字节数组视为包含 UTF-8 编码文本来创建字符串。声明此方法要抛出的异常之一(这是一个受检查的异常)是UnsupportedEncodingException。
然而,这是不可能的。java虚拟机规范保证UTF-8可用。这段代码是完全有效的:
try {
new String(someByteArray, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalError("Your JDK is corrupt. Reinstall it. Hard-crashing now because continuing in the face of a corrupt JDK install is asking for trouble");
// possibly log that and run System.exit(0) instead!
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们开始编写这种代码,那么它会在哪里结束呢?你应该写:
String x = ...;
try {
x = x.toLowerCase();
} catch (IllegalStateException e) {
throw new RuntimeError("Your JDK is corrupt");
}
Run Code Online (Sandbox Code Playgroud)
这看起来相当愚蠢。因此,客观上,尝试实际处理“除非 JDK 损坏否则不可能”的情况UnsupportedEncodingException同样愚蠢,所以我在这里注入的唯一主观的事情是,尝试处理损坏的 JDK 是愚蠢的;你真的不能,如果你尝试,你最终会到处写不可测试的不合逻辑的 try/ catch。
这种事情正是 SneakyThrows 的闪光点。因为您既不想编写 try/catch,也不想通过列出实际上不可能发生的异常类型来增加调用者的负担。
在 lombok 的 SneakyThrows 功能发布时,该new String(someByteArray, StandardCharsets.UTF_8)API 还不存在!__
它现在存在的事实是一件好事:我想说,任何在程序员知道它不可能发生的情况下抛出已检查异常的 API 都是非常粗糙的 API。我很高兴你现在不必再处理它了。
不管怎样,确实有很多图书馆存在这种情况。
这是有效的java:
public static void main(String[] args) throws Exception {}
Run Code Online (Sandbox Code Playgroud)
main方法可以做到这一点。我强烈建议所有 Java 程序员养成这样做的习惯。检查异常的概念(例如,调用者需要“担心”它们)对于库函数(任何旨在被理解为 API 而不是实现的代码而编写的代码,并且由外部代码使用)非常有用。编写它的团队的直接限制。这并不一定意味着“由其他人”,但可能意味着:由我们正在编写的应用程序的另一个模块化层)。
不过,这对于应用程序代码来说是愚蠢的!
许多异常实际上是无法处理的。库不知道这意味着什么(例如,代码new FileInputStream不知道是否可以处理“找不到文件” - 也许这是 GUI 应用程序的“文件打开”对话框,GUI 可以简单地告诉用户他们输入了一个不存在的文件名,需要选择其他内容 - 这就是处理它)。然而,应用程序代码总是知道的。他们可以,或者不能。通常,他们不能。
那么,当遇到无法处理的异常时该怎么办?
您实际上只有一个选择:将其向前抛出并希望您上面的某个层(调用链中某处的调用者)可以处理它。任何其他行为都是愚蠢的,并且会导致糟糕的代码、糟糕的行为。例如,这种现象无处不在,但却是邪恶的:
try {
stuff;
catch (Thingie e) {
e.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)
这是 basic 的 java 版本ON ERROR RESUME NEXT:当发生错误时,将有关它的一半信息扔进垃圾箱,使任何其他代码无法尝试处理它,将其记录到很少有人检查的地方,然后保留就好像没有发生任何错误一样,这可能意味着如果确实出现问题,则会出现 85 条堆栈跟踪,因为所有代码都是这样编写的,并且通常在发生异常时“继续前进”仅意味着很快就会发生更多异常。
当我们谈到入口点时,这不仅是,而且对于 Web 框架来说也是如此,作为 Web 框架开始调用代码main的点的许多方法(即,这是 URL 的入口点处理程序)是所有“入口点”和“应用程序代码”,以下是关键见解:/users/{username}
处理通用的、业务逻辑无法处理的错误情况并非易事。
例如,假设您有一个 Web 框架入口点,它所做的第一件事就是打开数据库来检查用户的凭据。如果数据库完全崩溃,对于网络浏览用户来说,发生的 SQLException 会导致什么结果?当然是“错误消息”,但情况比这更复杂。您最希望发生的是:
这绝对不是小事!您不想在整个代码库中将所有逻辑编写在 5000 个单独的 try/catch 块中。
处理此类通用错误响应的最佳位置是在调用入口点的任何代码中。
对于main,这已经是它的工作原理了:您可以抛出任何您想要的东西,处理它的方式java.exe(这是线程的默认异常处理程序)是打印类型、消息、堆栈跟踪和因果链,然后杀死该线程。
然而,很多很多框架都搞砸了。例如,servlet 框架要求您抛出ServletException. 他们真是太傻了。Lombok@SneakyThrows可以通过让您抛出这些异常来帮助您。通常(这包括 servlet),它工作得很好 - servlet 入口点运行程序实际上捕获了所有内容。
SneakyThrows 还有第三种有用但不太建议的用法:
作为代码库,它不太漂亮,但是在某些情况下,确实可能会发生异常,但很少会发生(根据经验/情况),虽然您确实应该捕获它们并将它们包装在正确类型的异常中,但您正在努力实现里程碑版本(例如概念证明),虽然您不想编写一次性代码,但您将留下非关键内容(对于第一个版本的工作并不重要)记录为“稍后修复” '。在这种情况下,偷偷摸摸的抛出比所有替代方案都要好,因为偷偷摸摸的抛出根本不会干扰异常。
| 归档时间: |
|
| 查看次数: |
960 次 |
| 最近记录: |