简体   繁体   English

Java异常包装:糟糕的做法?

[英]Java exceptions wrapping: bad practice?

Coming from a PHP world where there's only one way to write exceptions handling.. I found the wrapping of exceptions in Java a bit "ugly": 来自PHP世界,只有一种方法可以编写异常处理..我发现Java中的异常包装有点“丑陋”:

public void exampleOneException(String input) throws MyBusinessException {
    try {
        // do something
    } catch (NumberFormatException e) {
        throw new MyBusinessException("Error...", e);
    }
}

I prefer to use this style instead: 我更喜欢使用这种风格:

public void exampleTwoException() {
    try {
        // do something
    } catch (MyBusinessException e) {
        log.error("Error...: " + e);
    } catch (NumberFormatException e) {
        log.error("Error...: " + e);
    }
}

Is there any difference or best practices on those approaches on handling exceptions? 这些处理例外的方法有什么不同或最佳做法吗?

These are both valid approaches for two different scenarios. 这些都是两种不同场景的有效方法。

In the first scenario, the method can't do anything intelligent about the exception, but it has to "report" it upwards. 在第一种情况下,该方法无法对异常做任何智能,但必须向上“报告”它。 That way the caller can catch the exception and decide what to do with it (eg cancel the flow, pop a message up to the user, log it, etc.) 这样调用者就可以捕获异常并决定如何处理异常(例如取消流程,向用户弹出消息,记录消息等)

In the second scenario, you catch the exception and log it, effectively hiding the failure from the caller. 在第二种情况下,您捕获异常并将其记录下来,从而有效地将错误从调用方隐藏起来。 This type of handling may be useful if the caller doesn't really care if the operation succeeded or not. 如果调用者并不真正关心操作是否成功,则此类处理可能很有用。

The first example would generally be seen as the better approach. 第一个例子通常被视为更好的方法。

You should also not consider the MyBusinessException to be wrapping the NumberFormatException , but rather the NumberFormatException is the cause of the MyBusinessException . 您也不应该将MyBusinessException视为包装 NumberFormatException ,而是NumberFormatExceptionMyBusinessException原因

Exceptions should be appropriate for the interface being exposed. 异常应该适合于公开的接口。 A caller of your interface should not need to know, or deal with, the implementation details. 接口的调用者不应该知道或处理实现细节。 Unless a NumberFormatException genuinely makes sense as a type of error when calling exampleOneException , it should be translated into a more suitable exception. 除非NumberFormatException在调用exampleOneException时作为一种错误真正有意义,否则它应该被转换为更合适的异常。

A more concrete example typically includes differing implementations, where the users of the interface should not be required to deal with the implementation specifics (which may not even be known at compile time). 更具体的示例通常包括不同的实现,其中不应要求接口的用户处理实现细节(在编译时甚至可能不知道)。

interface MyRepository {
    Object read(int id) throws ObjectNotFoundException;
}

// a sql backed repository
class JdbcRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (SQLException ex) {
            throw new ObjectNotFoundException(ex);
        }
    }
 }

// a file backed repository
class FileRepository implements MyRepository {
    public Object read(int id) throws ObjectNotFoundException {
        try { ... }
        catch (FileNotFoundException ex) {
            throw new ObjectNotFoundException(ex)
        }
    }
}

Because the interface declares the types of errors it can return, the clients of that interface can be consistent and rational. 因为接口声明了它可以返回的错误类型,所以该接口的客户端可以是一致且合理的。 Adding code to handle FileNotFoundException and SQLException then the actual implementation may be either, or neither, is not fantastic. 添加代码来处理FileNotFoundExceptionSQLException然后实际的实现可能是,或者两者都不是很棒。

Consider if there were multiple places in the implementation of FileRepository which could throw FileNotFoundException . 考虑FileRepository的实现中是否存在可能抛出FileNotFoundException多个位置。 Does that imply each and every one of them implies object not found ? 这是否意味着每一个都意味着找不到对象

When considering option two, exampleTwoException , it's important to realise that your catch block is effectively stating that the effects of whatever error occurred have been mitigated. 在考虑选项二, exampleTwoException ,重要的是要意识到你的catch块有效地说明已经减轻了发生的任何错误的影响。 There are cases where checked exceptions are reasonably ignored, but it is far more likely that a simple 某些情况下,检查的异常会被合理地忽略,但更有可能是简单的

try { ... }
catch (SomeException ex) {
    log.error("caught some exception", ex);
}

is actually the result of a developer not considering the consequences of the exception, or the code should have included a FIXME . 实际上是开发人员没有考虑异常后果的结果,或者代码应该包含FIXME

This is even more true when you see catch (Exception ex) , or the unconscionable catch (Throwable ex) . 当你看到catch (Exception ex)或者unconscableable catch (Throwable ex)时更是如此。

And finally, would you want to be the person to dig through an application finding all the places where you need to add (yet) another catch block to handle a new implementation? 最后,您是否希望成为挖掘应用程序的人,找到您需要添加(还)另一个catch块以处理新实现的所有位置? Perhaps that's a cause of catch (Exception ex) ... 也许这是catch (Exception ex)的原因catch (Exception ex) ......

Always throw exceptions appropriate to the abstraction 始终抛出适合抽象的异常

I would say both NumberFormatException and MyBusinessException are useful but in different cases. 我会说NumberFormatExceptionMyBusinessException都有用,但在不同的情况下。

They usually appear at different levels of class hierarchy: for example NumberFormatException is a lower-level exception and you might not want to expose it at a higher level (eg user interface) if the user of it has no power to recover from it. 它们通常出现在不同级别的类层次结构中:例如, NumberFormatException是一个较低级别的异常,如果用户无法从中恢复,您可能不希望在更高级别(例如用户界面)公开它。 In this case it is more elegant to just throw MyBusinessException and display an informative message that explains for example that something in a previous step was badly supplied or some internal processing error occurred and he/she needs to restart the process. 在这种情况下,抛出MyBusinessException并显示信息性消息更为优雅,例如说明前一步骤中的某些内容供应不当或发生了一些内部处理错误,他/她需要重新启动该过程。

On the other hand, if your function is used at an intermediate-level (eg API) and the developer has the means to recover from the exceptional behavior, NumberFormatException is more useful, as it can be dealt with programmatically and the flow of the application might continue with minimal interruption (eg supply a default valid number). 另一方面,如果您的函数在中间级别(例如API)使用并且开发人员具有从异常行为中恢复的方法,则NumberFormatException更有用,因为它可以通过编程方式处理并且应用程序的流程可能会以最小的中断继续(例如,提供默认的有效数字)。 Alternatively, this can indicate a flaw/bug in the code that should be fixed. 或者,这可以指示代码中应该修复的缺陷/错误。

For details about how to follow best practice in using exceptions, read Item 61 - Throw exceptions appropriate to the abstraction from Effective Java by Joshua Bloch . 有关如何遵循使用异常的最佳实践的详细信息,请阅读Item 61 - Throw exceptions appropriate to the abstraction Joshua Bloch的Effective Java Item 61 - Throw exceptions appropriate to the abstraction

From the perspective of clean and solid code you are not driving to the right direction. 从干净和坚固的代码的角度来看,你并没有朝着正确的方向前进。
Firstly you do not need to use try/catch for an Exception that is already thrown by the method's header. 首先,您不需要将try / catch用于方法标头已抛出的Exception So in your first case the code should be like below: 所以在你的第一种情况下,代码应如下所示:

 public void exampleOneException(String input) throws MyBusinessException {
    // do something
 }

This is prettier isn't it? 这比较漂亮不是吗? In the above case, if the MyBusinessException would occur then the exception will be swallowed so the user will never see what happened. 在上面的例子中,如果发生MyBusinessException ,那么将吞下异常,因此用户永远不会看到发生了什么。 If you want this to happen then this is your best practice. 如果您希望这种情况发生,那么这是您的最佳实践。

The second case is clear. 第二种情况很清楚。 You catch two different exceptions and you handle them (by logging) which is a better practice generally speaking. 您可以捕获两个不同的异常并处理它们(通过记录),这通常是一种更好的做法。

So the best practice of your two different cases depends on what you want to achieve. 因此,两种不同情况的最佳实践取决于您想要实现的目标。 Regarding beauty, this is totally subjective. 关于美,这是完全主观的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM