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

问题描述:

我对包装已检查异常的正确方法以及番石榴的方式有一个相当详细的问题。 (道歉的长度,但我想我的思维过程下来)java:包装已检查异常的标准方法


标准Runnable接口看起来是这样的:

public interface Runnable 
{ 
    public void run(); 
} 

其中run()不能抛出checked异常。所以如果我想要一个Runnable这是用来包装任务,抛出检查异常,我打算有事情,调用Runnable.run()处理这些异常,而不是在Runnable.run()本身,我不得不包装异常一个未经检查的例外。

等了一会儿,我使用:

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

,然后我可以在上层处理的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); 
    } 
} 

,然后我可以做到这一点:

/* 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()); 
} 

它似乎很好。

但现在我想,如果我想在库和使用库的应用程序中使用它,现在它们都依赖于WrappedException,所以它应该真的在基础库中,我可以包括到处。

这让我想,也许Guava在某个地方有一个标准的WrappedException类,因为我现在默认包含Guava作为依赖项。所以,我可以做

throw new WrappedException(e); 

throw Exceptions.wrap(e); 

Exceptions.rethrow(e); 

我只是看了看四周的番石榴,发现Throwables具有Throwables.propagate()看起来相似,但它只是包装检查的异常在RuntimeException中,而不是RuntimeException的特殊子类。

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

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

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

所以在我的上面代码,不会错误日志,我不得不做这样的事情:

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(); 
} 

的理念,正确的感觉对我来说,但执行的感觉不好。有没有更好的方法来处理包装的异常?


相关的问题:

+2

您的顶级处理代码除了日志之外还能做什么?当您记录链式异常的堆栈跟踪时,无论如何都会得到整个跟踪,因此无论它是否包装都无关紧要。 – artbristol 2011-06-08 21:31:38

+0

如果您只是记录日志,那么无论如何您都将记录所有内容,如果您正在进行一些实际处理,那么只需抓住或尝试解开可处理的异常。 'getCause'是你展开所需的全部。 – trutheality 2011-06-08 21:47:42

+0

@artbristol,@trutheality:是的,我想在RuntimeException上调用logError()。但是我的应用程序中logError()的另一部分是给用户的显示,我确实需要显示最有用的消息而没有任何噪声。他们不会通过异常堆栈来判断出了什么问题,而是要看看这个消息,如果他们能够自己发现问题,那么很好,否则我会得到“我有一个RuntimeException,我该怎么办?“ – 2011-06-08 21:55:58

春天是我比较类似的东西知道的唯一库。他们嵌套异常:NestedRuntimeExceptionNestedCheckedException。这些例外有有用的方法,如getMostSpecificCause()contains(Class exType)。他们的getMessage()方法返回原因的消息(如果包装异常已经有消息,它会被追加)。

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

这就是说,它不是很复杂的代码,我相信你可以在你的代码库中做类似的事情。不需要为此添加Spring依赖项。

如果我是你,我不会尽快“解开”例外情况,除非你真的可以在那里和那里处理它们。我会让他们冒泡(包装或不包装),用全局的ExceptionHandler来查看他们的因果链,找到第一个“有意义”的异常,并提取一个智能错误信息。如果不能这样做,我只会打印“技术错误”,并在错误消息的细节或某种日志中添加整个堆栈跟踪,以便进行错误报告。然后,您将修复该错误并/或抛出一个更有意义的异常。

番石榴的Throwables.getCausalChain()也可能会感兴趣的简化异常处理:

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

编辑:

我想多一点关于你的问题,我想你不应该真正担心关于用特定的“WrapperException”类型封装你的异常。你应该用最有意义的方法来包装它们:简单的RuntimeException(Guava的Throwables.propagate()可能在那里很有用),带有附加错误信息的RuntimeException,或者适当时更有意义的异常类型。

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

通常,最好避免使用未经检查的异常来打包检查的异常(请参阅herehere)。一些方式来解决它:

  1. 使用Callable而不是Runnable的
  2. 使用像波希米亚的Executors框架建议
  3. 子类Runnable(或任何接口/你使用类),并添加一些方法来检查对于事后运行中的异常,可能是方法public Exception getRunException()或类似方法。

如果你必须包装一个检查的异常,我认为最好的办法就像你已经做到了,定义一个RuntimeException的子类。但如果可能的话,我会尽量避免它。

+2

我认为Runnable只是一个例子来说明包装检查异常的需要。 – 2011-06-09 02:17:51

+0

@Steven:哟,你懂了 – 2011-06-09 03:15:51

+0

我明白了,我修改了我的答案 – 2011-06-09 19:59:51