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

时间:2022-06-03 22:41:34

I've seen this topic arise sometimes in the past but even after Googling about it, I still can't figure out what would be a good and elegant way to deal with it, so here it goes.

我已经看到过去有时会出现这个话题,但即使在谷歌搜索之后,我仍然无法弄清楚什么是一个优雅和优雅的方式来处理它,所以在这里。

Say I have some code that throws various exceptions...

假设我有一些引发各种异常的代码......

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

... and as we can see, I just wrap these exceptions and throw a new one saying that something went wrong within my API.

......正如我们所看到的,我只是将这些异常包装起来并抛出一个新的异常,说明我的API中出了问题。

This seems to me a overly repetitive code, so one would simply catch an Exception type and handle wrap it and throw a new one.

在我看来,这是一个过于重复的代码,因此只需捕获一个Exception类型并处理它并将其抛出一个新的代码。

try {
  /* some code that throws these exceptions */
} catch (Exception e) {
    throw new MyAPIException("Something went wrong", e);
}

In this case it stays way more simpler, but the thing is we'd be catch also every RuntimeException. Given this, we could catch-re-throw the RuntimeException so we could avoid this.

在这种情况下,它保持更简单,但问题是我们也会捕获每个RuntimeException。鉴于此,我们可以捕获 - 重新抛出RuntimeException,以便我们可以避免这种情况。

try {
  /* some code that throws some exceptions */
} catch (RuntimeException e) {
    throw e;
} catch (Exception e) {
    throw new MyAPIException("Something went wrong", e);
}

Its a bit bulky but it does the trick. Now theres another little issue about the catch(Exception e), that if my inner API throws another MyAPIException it would also be caught, wrapped and throwed within another MyAPIException. In this particular case we could also catch MyAPIException and re-throw it.

它有点笨重,但它的确如此。现在关于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);
}

Well, its getting messy again, but in this case we prevent wrapping MyAPIException and simply re-throw it. But, theres also another issue with the catch (Exception e) block that is if the inner API changes and start throwing another kind of exception (some other than these 4 mentioned above), the compiler wouldn't say anything about it and we wouldn't have a clue. Not that this would be a major problem, since probably I would treat it in the same manner.

好吧,它再次变得混乱,但在这种情况下,我们阻止包装MyAPIException并简单地重新抛出它。但是,还有另一个问题,即catch(Exception e)块,如果内部API发生变化并开始抛出另一种异常(除了上面提到的这些之外的一些),编译器就不会对它有任何意义而且我们不会我有一个线索。这不是一个主要问题,因为我可能会以同样的方式对待它。

With this scenario, I think the question is, which one is better and are there better options?

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

6 个解决方案

#1


5  

Since you are stuck with Java5 and you use a proprietary exception, why not put all that exception logic into the exception class.

由于您使用Java5并且使用专有异常,为什么不将所有异常逻辑放入异常类中。

Useage

try
{
     // some code that might throw one of several exceptions
}
catch ( Exception cause )
{
     MyAPIException . handle ( cause ) ;
}

MyAPIException contains the logic

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
          }
    }
}

#2


2  

Have more than one MyApiException. If you have every exception as MyApiException, it becomes a bit difficult for you and for others too to read your code. Naming it properly would also be crucial. Plus you do not always want to catch and rethrow it. If thats the case, simply declare a throws in method signature.

有多个MyApiException。如果您将每个异常都作为MyApiException,那么您和其他人也很难读取您的代码。正确命名也很重要。另外,你并不总是想抓住并重新抛出它。如果是这种情况,只需在方法签名中声明一个throws。

Plus you cannot fix on either of a simple catch or multiple catches. Its more of a judgmental decision IMO as there are exceptions which are fatal (as in the entire program has to halt) to more easy-to-handle types.

另外,你不能解决简单的捕获或多次捕获。它更像是IMO的一个判断性决定,因为有些例外是致命的(如整个程序必须停止的那样)更容易处理的类型。

I do not see anything wrong at all when you have a bunch of throws clauses in your API. It is a much neater implementation. So what if the caller has to handle the exceptions that you've defined. If a method in your API may throw an exception and something has to be done about it, then the caller should have to handle it. Anyways, your documentation of that particular exception has to be clear in order not to make the caller confused.

当你在API中有一堆throws子句时,我没有看到任何错误。这是一个更简洁的实现。那么如果调用者必须处理您定义的异常,该怎么办呢?如果API中的方法可能抛出异常并且必须对其进行某些操作,则调用者应该必须处理它。无论如何,您必须清楚您对该特定异常的文档,以免使调用者感到困惑。

One more factor that decides this is the complexity of the API. Sometime back I had written an API which was moderately complex and I had to deal with the same issues like the one that you've presented. I had written few custom exceptions for few methods. I am sure you will not end up throwing exceptions for all the public method that the API exposes, but there are places which they are inevitable.

决定这一点的另一个因素是API的复杂性。不久之后,我写了一个中等复杂的API,我不得不处理与你提出的问题相同的问题。我为几个方法编写了一些自定义异常。我相信你最终不会为API公开的所有公共方法抛出异常,但有些地方是不可避免的。

At last, if you feel having too many custom exceptions a bitter choice, you can have one single exception with clearly documented error codes. This way the caller can handle the exception and deal with the error codes as he sees fit.

最后,如果您觉得有太多的自定义异常是一个痛苦的选择,那么您可以使用明确记录的错误代码进行一个例外。这样调用者可以处理异常并按照他认为合适的方式处理错误代码。

#3


2  

In JAVA 7 you will be able to do something like

在JAVA 7中,你将能够做类似的事情

try {
  /* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException , FactoryException, MismatchedDimensionException , TransformException e) {
    throw new MyAPIException("Something went wrong", e);
}

So perhaps the best answer is to chill out for a while and then upgrade to JAVA 7 when it becomes available.

所以也许最好的答案是放松一段时间,然后在可用时升级到JAVA 7。

#4


1  

OK guys, here is what I came up with... It's not that elegant but I think is a bit better than having various catches.

好吧,伙计们,这就是我想出来的......这不是那么优雅,但我认为比拥有各种优势要好一些。

First, we have an abstract class called ExceptionHandler.

首先,我们有一个名为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);
    }

}

Along with it, we have this simple runtime exception called UnhandledException

除此之外,我们还有一个名为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);
    }


}

With it we can use these to handle exceptions like this...

有了它,我们可以使用它们来处理这样的异常......

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

What you guys think?

你们觉得怎么样?

#5


0  

It all depends what you you want to do. If all you want to do is throw new MyException("Something went wrong", e); then one catch all will do.

这一切都取决于你想做什么。如果您想要做的就是抛出新的MyException(“出错了”,e);然后一个人都会抓住。

#6


0  

Below is another approach and a usage example. The MyAPIException class has a handle method. The handle method will handle any Exception.

以下是另一种方法和使用示例。 MyAPIException类有一个handle方法。 handle方法将处理任何Exception。

  1. If the Exception is a Runnable or a MyAPIException, handle will throw it without wrapping it.
  2. 如果Exception是Runnable或MyAPIException,句柄将抛出它而不包装它。

  3. If not, the handle method will test whether assertions are enabled. If assertions are enabled, the handle method will test to see if the exception is assignable from one of the supported exception types, throwing an AssertionError if it is not assignable. (If assertions are not enabled, the handle method ignores the supported exception types parameter.)
  4. 如果没有,handle方法将测试是否启用断言。如果启用了断言,则handle方法将测试是否可以从其中一个受支持的异常类型中分配异常,如果不可分配则抛出AssertionError。 (如果未启用断言,则handle方法将忽略支持的异常类型参数。)

  5. Finally - if it reaches this point - the handle method will wrap the exception in a MyAPIException and throw it.
  6. 最后 - 如果达到这一点 - handle方法将异常包装在MyAPIException中并抛出它。

When you test your code, run it with assertions enabled. In production, run it with assertions disabled.

测试代码时,请在启用断言的情况下运行它。在生产中,在禁用断言的情况下运行它。

<soapbox> If you use this technique, you will have to test for errors that the compiler would have otherwise caught.</soapbox>

如果您使用此技术,则必须测试编译器本来会捕获的错误。

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

This is an example of usage. You can run it with assertions enabled/disabled and with parameters 1,2,3,4 to see how it handles different situations.

这是一个使用示例。您可以在启用/禁用断言并使用参数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 ) ;
        }
    }
}

#1


5  

Since you are stuck with Java5 and you use a proprietary exception, why not put all that exception logic into the exception class.

由于您使用Java5并且使用专有异常,为什么不将所有异常逻辑放入异常类中。

Useage

try
{
     // some code that might throw one of several exceptions
}
catch ( Exception cause )
{
     MyAPIException . handle ( cause ) ;
}

MyAPIException contains the logic

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
          }
    }
}

#2


2  

Have more than one MyApiException. If you have every exception as MyApiException, it becomes a bit difficult for you and for others too to read your code. Naming it properly would also be crucial. Plus you do not always want to catch and rethrow it. If thats the case, simply declare a throws in method signature.

有多个MyApiException。如果您将每个异常都作为MyApiException,那么您和其他人也很难读取您的代码。正确命名也很重要。另外,你并不总是想抓住并重新抛出它。如果是这种情况,只需在方法签名中声明一个throws。

Plus you cannot fix on either of a simple catch or multiple catches. Its more of a judgmental decision IMO as there are exceptions which are fatal (as in the entire program has to halt) to more easy-to-handle types.

另外,你不能解决简单的捕获或多次捕获。它更像是IMO的一个判断性决定,因为有些例外是致命的(如整个程序必须停止的那样)更容易处理的类型。

I do not see anything wrong at all when you have a bunch of throws clauses in your API. It is a much neater implementation. So what if the caller has to handle the exceptions that you've defined. If a method in your API may throw an exception and something has to be done about it, then the caller should have to handle it. Anyways, your documentation of that particular exception has to be clear in order not to make the caller confused.

当你在API中有一堆throws子句时,我没有看到任何错误。这是一个更简洁的实现。那么如果调用者必须处理您定义的异常,该怎么办呢?如果API中的方法可能抛出异常并且必须对其进行某些操作,则调用者应该必须处理它。无论如何,您必须清楚您对该特定异常的文档,以免使调用者感到困惑。

One more factor that decides this is the complexity of the API. Sometime back I had written an API which was moderately complex and I had to deal with the same issues like the one that you've presented. I had written few custom exceptions for few methods. I am sure you will not end up throwing exceptions for all the public method that the API exposes, but there are places which they are inevitable.

决定这一点的另一个因素是API的复杂性。不久之后,我写了一个中等复杂的API,我不得不处理与你提出的问题相同的问题。我为几个方法编写了一些自定义异常。我相信你最终不会为API公开的所有公共方法抛出异常,但有些地方是不可避免的。

At last, if you feel having too many custom exceptions a bitter choice, you can have one single exception with clearly documented error codes. This way the caller can handle the exception and deal with the error codes as he sees fit.

最后,如果您觉得有太多的自定义异常是一个痛苦的选择,那么您可以使用明确记录的错误代码进行一个例外。这样调用者可以处理异常并按照他认为合适的方式处理错误代码。

#3


2  

In JAVA 7 you will be able to do something like

在JAVA 7中,你将能够做类似的事情

try {
  /* some code that throws these exceptions */
} catch (NoSuchAuthorityCodeException , FactoryException, MismatchedDimensionException , TransformException e) {
    throw new MyAPIException("Something went wrong", e);
}

So perhaps the best answer is to chill out for a while and then upgrade to JAVA 7 when it becomes available.

所以也许最好的答案是放松一段时间,然后在可用时升级到JAVA 7。

#4


1  

OK guys, here is what I came up with... It's not that elegant but I think is a bit better than having various catches.

好吧,伙计们,这就是我想出来的......这不是那么优雅,但我认为比拥有各种优势要好一些。

First, we have an abstract class called ExceptionHandler.

首先,我们有一个名为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);
    }

}

Along with it, we have this simple runtime exception called UnhandledException

除此之外,我们还有一个名为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);
    }


}

With it we can use these to handle exceptions like this...

有了它,我们可以使用它们来处理这样的异常......

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

What you guys think?

你们觉得怎么样?

#5


0  

It all depends what you you want to do. If all you want to do is throw new MyException("Something went wrong", e); then one catch all will do.

这一切都取决于你想做什么。如果您想要做的就是抛出新的MyException(“出错了”,e);然后一个人都会抓住。

#6


0  

Below is another approach and a usage example. The MyAPIException class has a handle method. The handle method will handle any Exception.

以下是另一种方法和使用示例。 MyAPIException类有一个handle方法。 handle方法将处理任何Exception。

  1. If the Exception is a Runnable or a MyAPIException, handle will throw it without wrapping it.
  2. 如果Exception是Runnable或MyAPIException,句柄将抛出它而不包装它。

  3. If not, the handle method will test whether assertions are enabled. If assertions are enabled, the handle method will test to see if the exception is assignable from one of the supported exception types, throwing an AssertionError if it is not assignable. (If assertions are not enabled, the handle method ignores the supported exception types parameter.)
  4. 如果没有,handle方法将测试是否启用断言。如果启用了断言,则handle方法将测试是否可以从其中一个受支持的异常类型中分配异常,如果不可分配则抛出AssertionError。 (如果未启用断言,则handle方法将忽略支持的异常类型参数。)

  5. Finally - if it reaches this point - the handle method will wrap the exception in a MyAPIException and throw it.
  6. 最后 - 如果达到这一点 - handle方法将异常包装在MyAPIException中并抛出它。

When you test your code, run it with assertions enabled. In production, run it with assertions disabled.

测试代码时,请在启用断言的情况下运行它。在生产中,在禁用断言的情况下运行它。

<soapbox> If you use this technique, you will have to test for errors that the compiler would have otherwise caught.</soapbox>

如果您使用此技术,则必须测试编译器本来会捕获的错误。

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

This is an example of usage. You can run it with assertions enabled/disabled and with parameters 1,2,3,4 to see how it handles different situations.

这是一个使用示例。您可以在启用/禁用断言并使用参数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 ) ;
        }
    }
}