我曾经看到过这个主题有时会在过去出现,但即使在谷歌搜索about it之后,我仍然无法弄清楚什么是一种优雅而优雅的方式来处理它,所以就这样吧。 / p>
假设我有一些引发各种异常的代码......
try {
/* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException e) {
throw new MyAPIException("Something went wrong", e);
} catch (FactoryException e) {
throw new MyAPIException("Something went wrong", e);
} catch (MismatchedDimensionException e) {
throw new MyAPIException("Something went wrong", e);
} catch (TransformException e) {
throw new MyAPIException("Something went wrong", e);
}
...正如我们所看到的,我只是将这些异常包装起来并抛出一个新的异常,说明我的API中出了问题。
在我看来,这是一个过于重复的代码,因此只需捕获一个Exception类型并处理它并将其抛出一个新的。
try {
/* some code that throws these exceptions */
} catch (Exception e) {
throw new MyAPIException("Something went wrong", e);
}
在这种情况下,它会更简单,但问题是我们每次RuntimeException都会被捕获。鉴于此,我们可以捕获 - 重新抛出RuntimeException,以便我们可以避免这种情况。
try {
/* some code that throws some exceptions */
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new MyAPIException("Something went wrong", e);
}
它有点笨重,但它可以解决问题。现在关于catch(Exception e)的另一个小问题是,如果我的内部API抛出另一个MyAPIException,它也会被捕获,包装并在另一个MyAPIException中抛出。在这种特殊情况下,我们也可以捕获MyAPIException并重新抛出它。
try {
/* some code that throws some exceptions */
} catch (RuntimeException e) {
throw e;
} catch (MyAPIException e) {
throw e;
} catch (Exception e) {
throw new MyAPIException("Something went wrong", e);
}
好吧,它再次变得混乱,但在这种情况下,我们阻止包装MyAPIException并简单地重新抛出它。但是,还有另一个问题,即catch(Exception e)块,如果内部API发生变化并开始抛出另一种异常(除了上面提到的这4个之外),编译器就不会说任何关于它的事情,我们也不会我有一个线索。并不是说这会是一个主要问题,因为我可能会以同样的方式对待它。
在这种情况下,我认为问题是,哪一个更好,有更好的选择吗?
答案 0 :(得分:5)
由于您使用Java5并且使用专有异常,为什么不将所有异常逻辑放入异常类中。
使用率
try
{
// some code that might throw one of several exceptions
}
catch ( Exception cause )
{
MyAPIException . handle ( cause ) ;
}
MyAPIException包含逻辑
class MyAPIException extends Exception
{
private MyAPIException ( String message , Throwable cause ) { super ( message , cause ) ; }
private static void myAPIException ( Exception cause ) throws MyAPIException
{
throw new MyAPIException ( "Something Went Wrong" , cause ) ;
}
public static void handle ( Exception e ) throws MyAPIException
{
try
{
throw ( e ) ;
}
catch ( RuntimeException cause )
{
throw cause ;
}
catch ( MyAPIException cause )
{
throw cause ;
}
catch ( NoSuchAuthorityCodeException cause ) // repeat for other exceptions
{
myAPIException ( cause ) ;
}
catch ( Exception cause ) // this should not happen
{
assert false ; // or log it or throw a RuntimeException ... somehow signal that programming logic has failed
}
}
}
答案 1 :(得分:2)
有多个MyApiException
。如果您将每个例外都设为MyApiException
,那么您和其他人也很难阅读您的代码。正确命名也很重要。另外,你并不总是想抓住并重新抛出它。如果是这种情况,只需在方法签名中声明throws
。
另外,你不能修复一个简单的捕获或多个捕获。它更像是IMO的一个判断性决定,因为有些例外是致命的(如整个程序必须停止的那样)更容易处理的类型。
如果您的API中包含大量throws
子句,我看不出任何错误。这是一个更简洁的实现。那么如果调用者必须处理您定义的异常,该怎么办呢?如果API中的方法可能抛出异常并且必须对其进行某些操作,则调用者应该必须处理它。无论如何,您必须清楚您对该特定异常的文档,以免使调用者感到困惑。
决定这一点的另一个因素是API的复杂性。不久之前,我写过一个中等复杂的API,我不得不处理与你提出的问题相同的问题。我为几个方法编写了一些自定义异常。我相信你最终不会为API公开的所有公共方法抛出异常,但有些地方是不可避免的。
最后,如果您觉得有太多的自定义异常是一个痛苦的选择,那么您可以使用明确记录的错误代码进行一个例外。这样调用者就可以处理异常并按照他认为合适的方式处理错误代码。
答案 2 :(得分:2)
在JAVA 7中,您可以执行类似
的操作try {
/* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException , FactoryException, MismatchedDimensionException , TransformException e) {
throw new MyAPIException("Something went wrong", e);
}
所以也许最好的答案是放松一下,然后升级到JAVA 7。
答案 3 :(得分:1)
好的伙计们,这就是我想出来的......它不是那么优雅,但我认为比拥有各种捕获物要好一些。
首先,我们有一个名为 ExceptionHandler 的抽象类。
package my.api.pack;
import java.lang.reflect.ParameterizedType;
import java.util.LinkedHashSet;
import java.util.Set;
public abstract class ExceptionHandler<E extends Exception> {
Set<Class<? extends Exception>> exceptionClasses = new LinkedHashSet<Class<? extends Exception>>();
protected abstract void handler(Throwable t) throws E;
public ExceptionHandler<E> catches(Class<? extends Exception> clazz) {
exceptionClasses.add(clazz);
return this;
}
@SuppressWarnings("unchecked")
private Class<E> getGenericsClass() {
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<E>) parameterizedType.getActualTypeArguments()[0];
}
@SuppressWarnings("unchecked")
public final void handle(Throwable t) throws E, UnhandledException {
if (getGenericsClass().isInstance(t)) {
throw (E) t;
}
for (Class<? extends Exception> clazz : exceptionClasses) {
if (clazz.isInstance(t)) {
handler(t);
return;
}
}
throw new UnhandledException("Unhandled exception", t);
}
}
除此之外,我们还有一个名为 UnhandledException 的简单运行时异常
package my.api.pack;
public class UnhandledException extends RuntimeException {
private static final long serialVersionUID = -3187734714363068446L;
public UnhandledException(String message, Throwable cause) {
super(message, cause);
}
}
有了它,我们可以使用它们来处理这样的异常......
try {
/* some code that throws these exceptions */
} catch (Exception e) {
new ExceptionHandler<MyAPIException>() {
@Override
protected void handler(Throwable t) throws MyAPIException {
throw new MyAPIException("Something went wrong", t);
}
}.
catches(MismatchedDimensionException.class).
catches(NoSuchAuthorityCodeException.class).
catches(FactoryException.class).
catches(TransformException.class).
handle(e);
return null;
}
你们的想法是什么?
答案 4 :(得分:0)
这完全取决于你想做什么。如果你想做的只是throw new MyException("Something went wrong", e);
,那么所有人都可以做到。
答案 5 :(得分:0)
以下是另一种方法和使用示例。 MyAPIException类有一个handle方法。 handle方法将处理任何Exception。
测试代码时,请在启用断言的情况下运行代码。在生产中,在禁用断言的情况下运行它。
&LT;肥皂盒&GT;如果您使用此技术,则必须测试编译器本来会捕获的错误。&lt; / soapbox&gt;
class MyAPIException extends Exception
{
private static final long serialVersionUID = 0 ;
MyAPIException ( Throwable cause )
{
super ( cause ) ;
}
static void handle ( Exception cause , Class < ? > ... supportedExceptionTypes ) throws MyAPIException
{
try
{
throw ( cause ) ;
}
catch ( RuntimeException e )
{
throw ( e ) ;
}
catch ( MyAPIException e )
{
throw ( e ) ;
}
catch ( Exception e )
{
search :
try
{
assert false ;
}
catch ( AssertionError a )
{
for ( Class < ? > c : supportedExceptionTypes )
{
if ( c . isAssignableFrom ( e . getClass ( ) ) )
{
break search ;
}
}
assert false : e + " is not one of the supportedExceptionTypes : " + supportedExceptionTypes ;
}
MyAPIException error = new MyAPIException ( e ) ;
throw ( error ) ;
}
}
}
这是一个使用示例。您可以在启用/禁用断言并使用参数1,2,3,4运行它,以查看它如何处理不同的情况。
class Usage
{
public static void main ( String [ ] args ) throws MyAPIException
{
try
{
switch ( Integer . parseInt ( args [ 0 ] ) )
{
case 1 :
throw new RuntimeException ( ) ;
case 2 :
throw new SQLException ( ) ;
case 3 :
throw new MyAPIException ( null ) ;
case 4 :
throw new IOException ( ) ;
}
}
catch ( Exception cause )
{
System . out . println ( cause . getMessage ( ) ) ;
System . out . println ( cause . getCause ( ) ) ;
MyAPIException . handle ( cause , IOException . class ) ;
}
}
}