多个特定捕获或一个捕获所有?

时间:2010-08-31 11:53:34

标签: java exception-handling

我曾经看到过这个主题有时会在过去出现,但即使在谷歌搜索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个之外),编译器就不会说任何关于它的事情,我们也不会我有一个线索。并不是说这会是一个主要问题,因为我可能会以同样的方式对待它。

在这种情况下,我认为问题是,哪一个更好,有更好的选择吗?

6 个答案:

答案 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。

  1. 如果Exception是Runnable或MyAPIException,句柄将抛出它而不包装它。
  2. 如果没有,handle方法将测试是否启用了断言。如果启用了断言,则handle方法将测试是否可以从一个受支持的异常类型中分配异常,如果不可分配则抛出AssertionError。 (如果未启用断言,则handle方法将忽略支持的异常类型参数。)
  3. 最后 - 如果达到这一点 - handle方法将异常包装在MyAPIException中并抛出它。
  4. 测试代码时,请在启用断言的情况下运行代码。在生产中,在禁用断言的情况下运行它。

    &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 ) ;
            }
        }
    }
    
相关问题