简体   繁体   English

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

[英]java: standard method of wrapping checked exceptions

I have a fairly detailed question about the right way to wrap a checked exception, and the way that Guava does it. 我有一个相当详细的问题,关于包装已检查异常的正确方法,以及Guava的方式。 (Apologies for the length but I want to get my thought process down) (道歉,但我想让我的思考过程失败)


The standard Runnable interface looks like this: 标准的Runnable接口如下所示:

public interface Runnable
{
   public void run();
}

where run() can't throw a checked exception. 其中run()不能抛出已检查的异常。

So if I want to have a Runnable which 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. 然后我可以在上层处理RuntimeException。 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: 除了我想,我真正想要的是分别处理一个包装的异常,因为我知道它的语义是包装一个已检查的异常,所以我写了这个帮助类:

/**
 * 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. 这让我想到,也许Guava在某处有一个标准的WrappedException类,因为我现在默认将Guava包含为依赖。 So I can just do 所以我可以做到

throw new WrappedException(e);

or 要么

throw Exceptions.wrap(e);

or 要么

Exceptions.rethrow(e);

I just looked around in Guava and found Throwables which has Throwables.propagate() that looks similar, but it just wraps checked exceptions in a RuntimeException , rather than a special subclass of RuntimeException. 我只是在Guava中环顾四周,发现Throwables的 Throwables.propagate()看起来很相似,但它只是在RuntimeException包含已检查的异常,而不是RuntimeException的特殊子类。

Which approach is better? 哪种方法更好? Should I not be using a special WrappedException as compared with a RuntimeException? 与RuntimeException相比,我不应该使用特殊的WrappedException吗? My top-level code wants to know the topmost exception that adds informational value. 我的顶级代码想知道添加信息值的最重要的异常。

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: 相关问题:

Spring is the only library I know of with something relatively similar. Spring是我所知道的唯一一个相对类似的库。 They have nested exceptions: NestedRuntimeException and NestedCheckedException . 它们具有嵌套异常: NestedRuntimeExceptionNestedCheckedException These exceptions have useful methods such as getMostSpecificCause() or contains(Class exType) . 这些异常具有有用的方法,例如getMostSpecificCause()contains(Class exType) Their getMessage() method returns the cause's message (it's appended if the wrapping exception already has a message). 他们的getMessage()方法返回cause的消息(如果包装异常已经有消息,则会附加它)。

It's used in Spring's data access exception hierarchy. 它用在Spring的数据访问异常层次结构中。 The idea is that each Database vendor exposes different exceptions in their JDBC drivers. 这个想法是每个数据库供应商在其JDBC驱动程序中公开不同的异常。 Spring catches those and translates them into more generic DataAccessExceptions . Spring捕获它们并将它们转换为更通用的DataAccessExceptions Another benefit of this is that checked exceptions are automatically translated into runtime exceptions. 这样做的另一个好处是,已检查的异常会自动转换为运行时异常。

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. 我会让它们冒泡(包装或不包装),使用全局ExceptionHandler查看其因果链,找到第一个“有意义”的异常,并提取智能错误消息。 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. 然后,您将修复错误和/或抛出更有意义的异常。

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. 我更多地考虑了你的问题,我认为你不应该真的担心用特定的“WrapperException”类型包装你的异常。 You should wrap them using what makes the most sense: either a simple RuntimeException (Guava's Throwables.propagate() might be useful there), a RuntimeException with an additional error message, or a more meaningful exception type when appropriate. 您应该使用最有意义的东西来包装它们:要么是简单的RuntimeException (Guava的Throwables.propagate()可能在那里有用),带有附加错误消息的RuntimeException ,或者适当时更有意义的异常类型。

Java's causal chain mechanism will let you get to the root cause anyway. Java的因果链机制无论如何都会让你找到根本原因。 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. 编写一个全局异常处理程序来管理它,并在其他地方使用标准异常冒泡。

In general, it's best to avoid wrapping checked exceptions with an unchecked exception (see here and here ). 通常,最好避免使用未经检查的异常包装已检查的异常(请参阅此处此处 )。 Some ways to get around it: 一些解决方法:

  1. Use Callable instead of Runnable 使用Callable而不是Runnable
  2. Use the Executors framework like Bohemian suggested 使用像Bohemian建议的Executors框架
  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. Subclass 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 . 如果你必须包装一个已检查的异常,我认为最好的方法就是你已经这样做了,定义一个RuntimeException的子类。 But I'd try to avoid it if at all possible. 但是如果可能的话,我会尽量避免它。

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

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