java:包装检查异常的标准方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6285476/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 15:12:34  来源:igfitidea点击:

java: standard method of wrapping checked exceptions

javaexceptionguava

提问by Jason S

I have a fairly detailed question about the right way to wrap a checked exception, and the way that Guava does it. (Apologies for the length but I want to get my thought process down)

我有一个关于包装已检查异常的正确方法的相当详细的问题,以及 Guava 的处理方式。(抱歉篇幅过长,但我想把我的思考过程记下来)



The standard Runnable interface looks like this:

标准的 Runnable 接口如下所示:

public interface Runnable
{
   public void run();
}

where run()can't throw a checked exception.

whererun()不能抛出已检查的异常。

So if I want to have a Runnablewhich is used to wrap tasks which throw checked exceptions, and I intend to have the thing that calls Runnable.run()handle those exceptions, rather than in Runnable.run()itself, I have to wrap the exception in an unchecked exception.

因此,如果我想要一个Runnable用于包装抛出已检查异常的任务,并且我打算让调用Runnable.run()处理这些异常的东西而不是Runnable.run()它本身,我必须将异常包装在一个未检查的异常中。

So for a while I was using:

所以有一段时间我在使用:

Runnable r = new Runnable {
   @Override public void run()
   {
       try {
          doNastyStuff();
       }
       catch (NastyException e)
       {
          throw new RuntimeException(e);
       }
   }      
};

and then I can handle RuntimeException in an upper level. Except then I figured, that what I really want is to handle a wrapped exception separately, since I know its semantics are to wrap a checked exception, so I wrote this helper class:

然后我可以在上层处理 RuntimeException。除此之外我想,我真正想要的是单独处理一个包装的异常,因为我知道它的语义是包装一个检查过的异常,所以我写了这个帮助类:

/**
 * Wrapped exception: the purpose of this is just to wrap another exception,
 * and indicate that it is a wrapped exception
 */
public class WrappedException extends RuntimeException
{
    /**
     * @param t any throwable
     */
    public WrappedException(Throwable t)
    {
        super(t);
    }
}

and then I can do this:

然后我可以这样做:

/* place that produces the exception */
...
catch (NastyException e)
{
   throw new WrappedException(e);
}

...
/* upper level code that calls Runnable.run() */
try
{
   ...
   SomeOtherNastyCode();
   r.run();
   ...
}
catch (SomeOtherNastyException e)
{
   logError(e);
}
catch (WrappedException e)
{
   logError(e.getCause());
}

and it seems to work great.

它似乎很好用。

But now I'm thinking, well, if I want to use this in a library as well as an application that uses the library, now they both depend on WrappedException, so it should really be in a base library that I can include everywhere.

但现在我在想,好吧,如果我想在一个库和一个使用该库的应用程序中使用它,现在它们都依赖于 WrappedException,所以它真的应该在一个我可以包含在任何地方的基础库中。

Which makes me think, maybe Guava has a standard WrappedException class somewhere, since I now include Guava as a dependency by default. So I can just do

这让我想到,也许 Guava 在某处有一个标准的 WrappedException 类,因为我现在默认将 Guava 作为依赖项包含在内。所以我只能做

throw new WrappedException(e);

or

或者

throw Exceptions.wrap(e);

or

或者

Exceptions.rethrow(e);

I just looked around in Guava and found Throwableswhich has Throwables.propagate()that looks similar, but it just wraps checked exceptions in a RuntimeException, rather than a special subclass of RuntimeException.

我刚才看了一下四周番石榴,发现将Throwable具有Throwables.propagate()相似的是外表,但它只是包装检查的异常RuntimeException,而不是RuntimeException的一个特殊子类。

Which approach is better? Should I not be using a special WrappedException as compared with a RuntimeException? My top-level code wants to know the topmost exception that adds informational value.

哪种方法更好?与 RuntimeException 相比,我不应该使用特殊的 WrappedException 吗?我的顶层代码想知道增加信息价值的最顶层异常。

If I have a RuntimeException that wraps a NastyException that wraps a NullPointerException, the wrapping RuntimeException doesn't add informational value, and I don't care about it, so the error I would log would be the NastyException.

如果我有一个包装了 NastyException 的 RuntimeException,它包装了一个 NullPointerException,那么包装的 RuntimeException 不会添加信息值,我也不关心它,所以我要记录的错误将是 NastyException。

If I have an IllegalArgumentException that wraps a NastyException, the IllegalArgumentException does generally add informational value.

如果我有一个包含 NastyException 的 IllegalArgumentException,则 IllegalArgumentException 通常会添加信息值。

So in my top code that does error logging, I'd have to do something like this:

因此,在我执行错误日志记录的顶级代码中,我必须执行以下操作:

catch (RuntimeException re)
{
   logError(getTheOutermostUsefulException(re));
}

/** 
 * heuristics to tease out whether an exception
 * is wrapped just for the heck of it, or whether
 * it has informational value
 */
Throwable getTheOutermostUsefulException(RuntimeException re)
{        
   // subclasses of RuntimeException should be used as is
   if (re.getClass() != RuntimeException)
      return re;
   // if a runtime exception has a message, it's probably useful
   else if (re.getMessage() != null)
      return re;
   // if a runtime exception has no cause, it's certainly
   // going to be more useful than null
   else if (re.getCause() == null)
      return re;
   else
      return re.getCause();
}

The philosophy feels right for me, but the implementation feels bad. Is there a better way to handle wrapped exceptions?

这种理念对我来说是正确的,但实施起来感觉很糟糕。有没有更好的方法来处理包装异常?



related questions:

相关问题:

采纳答案by Etienne Neveu

Spring is the only library I know of with something relatively similar. They have nested exceptions: NestedRuntimeExceptionand NestedCheckedException. These exceptions have useful methods such as getMostSpecificCause()or contains(Class exType). Their getMessage()method returns the cause's message (it's appended if the wrapping exception already has a message).

Spring 是我所知道的唯一一个具有相对相似内容的库。它们有嵌套异常:NestedRuntimeExceptionNestedCheckedException。这些异常具有有用的方法,例如getMostSpecificCause()contains(Class exType)。他们的getMessage()方法返回原因的消息(如果包装异常已经有消息,则附加它)。

It's used in Spring's data access exception hierarchy. The idea is that each Database vendor exposes different exceptions in their JDBC drivers. Spring catches those and translates them into more generic DataAccessExceptions. Another benefit of this is that checked exceptions are automatically translated into runtime exceptions.

它用于 Spring 的数据访问异常层次结构。这个想法是每个数据库供应商在他们的 JDBC 驱动程序中公开不同的异常。Spring 捕获这些并将它们转换为更通用的DataAccessExceptions。这样做的另一个好处是检查异常会自动转换为运行时异常。

That being said, it's not very complicated code, and I'm sure you could do something similar in your code base. No need to add a Spring dependency just for that.

话虽如此,这并不是很复杂的代码,我相信你可以在你的代码库中做类似的事情。无需为此添加 Spring 依赖项。

If I were you, I wouldn't try to "unwrap" the exceptions too soon, unless you really can handle them right then and there. I would let them bubble up (wrapped or not), with a global ExceptionHandler that would look at their causal chain, find the first "meaningful" exception, and extract an intelligent error message. When unable to do so, I would just print "technical error" and add the whole stack trace in the detail of the error message or in some kind of log, for bug reports purpose. You'd then fix the bug and/or throw a more meaningful exception instead.

如果我是你,我不会过早地尝试“解开”异常,除非你真的可以立即处理它们。我会让它们冒泡(包装与否),使用全局 ExceptionHandler 来查看它们的因果链,找到第一个“有意义的”异常,并提取智能错误消息。如果无法这样做,我只会打印“技术错误”并在错误消息的详细信息或某种日志中添加整个堆栈跟踪,以用于错误报告目的。然后,您将修复错误和/或抛出更有意义的异常。

Guava's Throwables.getCausalChain()might also be of interest to simplify exception handling:

Guava 的Throwables.getCausalChain()也可能对简化异常处理感兴趣:

Iterables.filter(Throwables.getCausalChain(e), IOException.class));


Edit:

编辑:

I thought a little more about your problem, and I think you shouldn't really worry about wrapping your exceptions with a specific "WrapperException" type. You should wrap them using what makes the most sense: either a simple RuntimeException(Guava's Throwables.propagate()might be useful there), a RuntimeExceptionwith an additional error message, or a more meaningful exception type when appropriate.

我对你的问题考虑了更多,我认为你真的不应该担心用特定的“WrapperException”类型来包装你的异常。您应该使用最有意义的方式包装它们:一个简单的RuntimeException(GuavaThrowables.propagate()可能在那里有用),RuntimeException带有附加错误消息的一个,或者在适当的时候更有意义的异常类型。

Java's causal chain mechanism will let you get to the root cause anyway. You shouldn't need to worry about exception wrapping everywhere in your code. Write a global exception handler to manage it, and use standard exception bubbling everywhere else.

Java 的因果链机制无论如何都会让您找到根本原因。您不必担心代码中到处都是异常包装。编写一个全局异常处理程序来管理它,并在其他地方使用标准异常冒泡。

回答by Kevin K

In general, it's best to avoid wrapping checked exceptions with an unchecked exception (see hereand here). Some ways to get around it:

通常,最好避免使用未检查异常包装已检查异常(请参阅此处此处)。绕过它的一些方法:

  1. Use Callableinstead of Runnable
  2. Use the Executorsframework like Bohemian suggested
  3. Subclass Runnable(or whatever interface/class you're using) and add some way to check for exceptions during the run after the fact, maybe a method public Exception getRunException()or similar.
  1. 使用Callable而不是 Runnable
  2. 使用Executors像 Bohemian 建议的框架
  3. 子类Runnable(或您使用的任何接口/类)并添加一些方法来在事后运行期间检查异常,可能是方法public Exception getRunException()或类似方法。

If you must wrap a checked exception, I think the best way to do it is like you already do it, define a subclass of RuntimeException. But I'd try to avoid it if at all possible.

如果您必须包装已检查的异常,我认为最好的方法是像您已经这样做一样,定义RuntimeException. 但如果可能的话,我会尽量避免它。