我应该在 if-else 块中抛出异常吗?
Should I throw exceptions in an if-else block?
代码如下:
public Response getABC(Request request) throws Exception {
Response res = new Response();
try {
if (request.someProperty == 1) {
// business logic
} else {
throw new Exception("xxxx");
}
} catch (Exception e) {
res.setMessage(e.getMessage); // I think this is weird
}
return res;
}
这个程序运行良好。
我认为它应该重新设计,但是如何?
在 try 块中抛出异常并立即捕获它是没有意义的,除非 catch 块抛出不同的异常。
这样你的代码会更有意义:
public Response getABC(Request request) {
Response res = new Response();
if (request.someProperty == 1) {
// business logic
} else {
res.setMessage("xxxx");
}
return res;
}
如果您的业务逻辑(在条件为 true
时执行)可能会抛出异常,则只需要 try-catch 块。
如果你没有捕获异常(这意味着调用者必须处理它),你可以不使用 else
子句:
public Response getABC(Request request) throws Exception {
if (request.someProperty != 1) {
throw new Exception("xxxx");
}
Response res = new Response();
// business logic
return res;
}
如果您从方法中抛出异常,那么为什么还要捕获它呢?它要么是你 return 对 "xxxx" 消息的响应,要么是抛出异常让此方法的调用者处理它。
public Response getABC(Request requst) {
Response res = new Response();
if(request.someProperty == 1){
//business logic
else{
res.setMessage("xxxx");
}
}
return res;
}
或
public Response getABC(Request requst) throw Excetpions {
Response res = new Response();
if(request.someProperty == 1){
//business logic
else{
throw new Exception("xxxx");
}
return res;
}
public void someMethod(Request request) {
try {
Response r = getABC(request);
} catch (Exception e) {
//LOG exception or return response with error message
Response response = new Response();
response.setMessage("xxxx");
retunr response;
}
}
首先,重构工作方法时要更加小心——尤其是在执行手动重构时。也就是说,引入一个变量来保存 message
可能是改变设计的一种方式:
public Response getABC(Request requst) throw Excetpions {
String message = "";
try{
if(request.someProperty == 1){
//business logic
else{
message = "xxxx";
}
}catch(Exception e){
message = e.getMessage();
}
Response res = new Response();
res.setMessage(message);
return res;
}
假设 business logic
在成功时会自己 return。
故意抛出异常然后直接捕获似乎不对,
它可以像这样重新设计,
可以将 throw new Exception("xxxx");
更改为 res.setMessage("xxxx");
,
然后可以保留捕获异常的部分,以便捕获业务逻辑内部可能发生的异常。
public Response getABC(Request requst) {
Response res = new Response();
try{
if(request.someProperty == 1){
//business logic
else{
res.setMessage("xxxx");
}
}catch(Exception e){
res.setMessage(e.getMessage);
}
return res;
}
为什么在已经抛出 Checked Exception 时使用 try/catch 语句?
Checked exception 通常用于某些语言,如 C++ 或 Java,但不用于 Kotlin 等新语言。我个人限制使用它。
例如,我有一个这样的class:
class ApiService{
Response getSomething() throw Exception();
}
感觉干净且可读,但破坏了异常处理机制的实用性。实际上,getSomething()
不会抛出已检查的异常,但仍需要像它那样运行?当 ApiService 的上游有人知道如何处理像这样的 unpredictable 或 unpreventable 错误时,这会起作用。如果你真的知道如何处理它,那就继续使用类似下面的示例,否则,Unchecked Exception 就足够了。
public Response getSomething(Request req) throws Exception{
if (req.someProperty == 1) {
Response res = new Response();
// logic
} else {
thows Exception("Some messages go here")
}
}
我会鼓励这样做:
public Response getSomething(Request req){
if (req.someProperty == 1) {
Response res = new Response();
// logic
return res;
} else {
return ErrorResponse("error message"); // or throw RuntimeException here if you want to
}
}
为了获得更多见解,我之前提到的 Kotlin
不支持 Checked exception,原因有很多。
下面是JDK
实现的接口示例 StringBuilder
class:
Appendable append(CharSequence csq) throws IOException;
这个签名说了什么?它说每次我将字符串附加到某些东西(StringBuilder
、某种日志、控制台等)时,我必须捕获那些 IOExceptions
。为什么?因为它可能正在执行 IO
(Writer 也实现了 Appendable
)...所以它导致到处都是这种代码:
try {
log.append(message)
}
catch (IOException e) {
// Must be safe
}
这不好,请参阅 有效 Java,第 3 版,第 77 项:不要忽略异常。
看看这些链接:
- Checked and unchecked exception
- Java's checked exceptions were a mistake(罗德·沃尔德霍夫)
- The Trouble with Checked Exceptions(安德斯·海尔斯伯格)
异常机制有三个目的:
- 立即禁用正常程序流并返回调用堆栈,直到找到合适的 catch 块。
- 以异常类型、消息和可选的附加字段的形式提供上下文,catch 块代码可以使用这些字段来确定操作过程。
- 供程序员查看以进行取证分析的堆栈跟踪。 (这在过去制作起来非常昂贵)。
这是一个机制应该具备的很多功能。为了使程序尽可能简单 - 对于未来的维护者 - 因此,我们应该只在 确实 必须使用时才使用此机制。
在您的示例代码中,我希望任何 throw
语句都是非常严重的事情,表明出现了问题并且代码应该在某处处理这种紧急情况。在继续阅读程序的其余部分之前,我需要了解出了什么问题以及它有多严重。这里只是一个奇特的 return 字符串,我会挠头想 "Why was this necessary?" 并且可以更好地花费额外的努力。
所以这段代码并不尽如人意,但如果您也有时间进行完整测试,我只会更改它。更改程序流程可能会引入细微的错误,如果您需要修复任何内容,您需要牢记这些更改。
我认为您可能没有理解try/catch 的要点。该代码使用异常系统将任何异常消息冒泡给调用者。这可能位于嵌套调用堆栈的深处——而不仅仅是您正在查看的 "throws"。
换句话说,示例代码中的 "throws" 声明利用此机制向客户端传递消息,但几乎可以肯定它不是 [=22 的主要预期用户=]. (此外,这是传递此消息的一种草率、有点廉价的方式——它可能会导致混淆)
这个 return 值无论如何都不是一个好主意,因为异常通常没有消息并且可以重新包装......不过总比没有好。异常消息并不是最好的工具,但像这样在高级别处理异常仍然是一个好主意。
我的观点是,如果您重构此代码,请确保查找可能在您的代码库中的任何地方抛出的运行时异常(至少在消息处理期间调用的任何地方)——即便如此,您也应该保留 catch/return 消息作为一个包罗万象的消息,以防出现你没有预料到的运行时异常。您不必 return 错误 "Message" 作为您的响应消息——它可能是一些俏皮的 "We couldn't process your request at this time",但一定要将堆栈跟踪转储到日志中。您目前正在丢弃它。
同样,如果你想获得JVM在失败时返回的特定异常消息,那时候你可以在catch块中使用方法getMessage()或printStackTrace()的try-catch。所以在这里你可以修改你的代码:
public Response getABC(Request request) throws Exception {
Response res = new Response();
try {
if (request.someProperty == 1) {
// business logic
}
} catch (Exception e) {
res.setMessage(e.getMessage);
}
return res;
}
代码如下:
public Response getABC(Request request) throws Exception {
Response res = new Response();
try {
if (request.someProperty == 1) {
// business logic
} else {
throw new Exception("xxxx");
}
} catch (Exception e) {
res.setMessage(e.getMessage); // I think this is weird
}
return res;
}
这个程序运行良好。 我认为它应该重新设计,但是如何?
在 try 块中抛出异常并立即捕获它是没有意义的,除非 catch 块抛出不同的异常。
这样你的代码会更有意义:
public Response getABC(Request request) {
Response res = new Response();
if (request.someProperty == 1) {
// business logic
} else {
res.setMessage("xxxx");
}
return res;
}
如果您的业务逻辑(在条件为 true
时执行)可能会抛出异常,则只需要 try-catch 块。
如果你没有捕获异常(这意味着调用者必须处理它),你可以不使用 else
子句:
public Response getABC(Request request) throws Exception {
if (request.someProperty != 1) {
throw new Exception("xxxx");
}
Response res = new Response();
// business logic
return res;
}
如果您从方法中抛出异常,那么为什么还要捕获它呢?它要么是你 return 对 "xxxx" 消息的响应,要么是抛出异常让此方法的调用者处理它。
public Response getABC(Request requst) {
Response res = new Response();
if(request.someProperty == 1){
//business logic
else{
res.setMessage("xxxx");
}
}
return res;
}
或
public Response getABC(Request requst) throw Excetpions {
Response res = new Response();
if(request.someProperty == 1){
//business logic
else{
throw new Exception("xxxx");
}
return res;
}
public void someMethod(Request request) {
try {
Response r = getABC(request);
} catch (Exception e) {
//LOG exception or return response with error message
Response response = new Response();
response.setMessage("xxxx");
retunr response;
}
}
首先,重构工作方法时要更加小心——尤其是在执行手动重构时。也就是说,引入一个变量来保存 message
可能是改变设计的一种方式:
public Response getABC(Request requst) throw Excetpions {
String message = "";
try{
if(request.someProperty == 1){
//business logic
else{
message = "xxxx";
}
}catch(Exception e){
message = e.getMessage();
}
Response res = new Response();
res.setMessage(message);
return res;
}
假设 business logic
在成功时会自己 return。
故意抛出异常然后直接捕获似乎不对,
它可以像这样重新设计,
可以将 throw new Exception("xxxx");
更改为 res.setMessage("xxxx");
,
然后可以保留捕获异常的部分,以便捕获业务逻辑内部可能发生的异常。
public Response getABC(Request requst) {
Response res = new Response();
try{
if(request.someProperty == 1){
//business logic
else{
res.setMessage("xxxx");
}
}catch(Exception e){
res.setMessage(e.getMessage);
}
return res;
}
为什么在已经抛出 Checked Exception 时使用 try/catch 语句?
Checked exception 通常用于某些语言,如 C++ 或 Java,但不用于 Kotlin 等新语言。我个人限制使用它。
例如,我有一个这样的class:
class ApiService{
Response getSomething() throw Exception();
}
感觉干净且可读,但破坏了异常处理机制的实用性。实际上,getSomething()
不会抛出已检查的异常,但仍需要像它那样运行?当 ApiService 的上游有人知道如何处理像这样的 unpredictable 或 unpreventable 错误时,这会起作用。如果你真的知道如何处理它,那就继续使用类似下面的示例,否则,Unchecked Exception 就足够了。
public Response getSomething(Request req) throws Exception{
if (req.someProperty == 1) {
Response res = new Response();
// logic
} else {
thows Exception("Some messages go here")
}
}
我会鼓励这样做:
public Response getSomething(Request req){
if (req.someProperty == 1) {
Response res = new Response();
// logic
return res;
} else {
return ErrorResponse("error message"); // or throw RuntimeException here if you want to
}
}
为了获得更多见解,我之前提到的 Kotlin
不支持 Checked exception,原因有很多。
下面是JDK
实现的接口示例 StringBuilder
class:
Appendable append(CharSequence csq) throws IOException;
这个签名说了什么?它说每次我将字符串附加到某些东西(StringBuilder
、某种日志、控制台等)时,我必须捕获那些 IOExceptions
。为什么?因为它可能正在执行 IO
(Writer 也实现了 Appendable
)...所以它导致到处都是这种代码:
try {
log.append(message)
}
catch (IOException e) {
// Must be safe
}
这不好,请参阅 有效 Java,第 3 版,第 77 项:不要忽略异常。
看看这些链接:
- Checked and unchecked exception
- Java's checked exceptions were a mistake(罗德·沃尔德霍夫)
- The Trouble with Checked Exceptions(安德斯·海尔斯伯格)
异常机制有三个目的:
- 立即禁用正常程序流并返回调用堆栈,直到找到合适的 catch 块。
- 以异常类型、消息和可选的附加字段的形式提供上下文,catch 块代码可以使用这些字段来确定操作过程。
- 供程序员查看以进行取证分析的堆栈跟踪。 (这在过去制作起来非常昂贵)。
这是一个机制应该具备的很多功能。为了使程序尽可能简单 - 对于未来的维护者 - 因此,我们应该只在 确实 必须使用时才使用此机制。
在您的示例代码中,我希望任何 throw
语句都是非常严重的事情,表明出现了问题并且代码应该在某处处理这种紧急情况。在继续阅读程序的其余部分之前,我需要了解出了什么问题以及它有多严重。这里只是一个奇特的 return 字符串,我会挠头想 "Why was this necessary?" 并且可以更好地花费额外的努力。
所以这段代码并不尽如人意,但如果您也有时间进行完整测试,我只会更改它。更改程序流程可能会引入细微的错误,如果您需要修复任何内容,您需要牢记这些更改。
我认为您可能没有理解try/catch 的要点。该代码使用异常系统将任何异常消息冒泡给调用者。这可能位于嵌套调用堆栈的深处——而不仅仅是您正在查看的 "throws"。
换句话说,示例代码中的 "throws" 声明利用此机制向客户端传递消息,但几乎可以肯定它不是 [=22 的主要预期用户=]. (此外,这是传递此消息的一种草率、有点廉价的方式——它可能会导致混淆)
这个 return 值无论如何都不是一个好主意,因为异常通常没有消息并且可以重新包装......不过总比没有好。异常消息并不是最好的工具,但像这样在高级别处理异常仍然是一个好主意。
我的观点是,如果您重构此代码,请确保查找可能在您的代码库中的任何地方抛出的运行时异常(至少在消息处理期间调用的任何地方)——即便如此,您也应该保留 catch/return 消息作为一个包罗万象的消息,以防出现你没有预料到的运行时异常。您不必 return 错误 "Message" 作为您的响应消息——它可能是一些俏皮的 "We couldn't process your request at this time",但一定要将堆栈跟踪转储到日志中。您目前正在丢弃它。
同样,如果你想获得JVM在失败时返回的特定异常消息,那时候你可以在catch块中使用方法getMessage()或printStackTrace()的try-catch。所以在这里你可以修改你的代码:
public Response getABC(Request request) throws Exception {
Response res = new Response();
try {
if (request.someProperty == 1) {
// business logic
}
} catch (Exception e) {
res.setMessage(e.getMessage);
}
return res;
}