Возможно ли собрать несколько исключений в одно пользовательское исключение?

#java #exception

#java #исключение

Вопрос:

У меня есть повторяющиеся исключения, которые необходимо создавать из-за использования reflect API для целей рефакторинга:

 throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException
  

Эти исключения всегда присутствуют в методе, в котором я использую переработанные методы, возможно ли это сделать
что-то вроде этого вместо:

 throws CustomException
  

для обработки всех исключений, упомянутых выше?
Спасибо

Комментарии:

1. конечно, это так. просто перехватите все вышеперечисленное и в блоке catch создайте свое CustomException

2. Взгляните на Exception конструкторы, которые уже предоставляют средства для переноса / вложения любого исключения в другое. Конечно, тот, кто перехватит ваше исключение, должен будет распаковать их, чтобы получить больше информации.

Ответ №1:

ДА. Есть два способа сделать это:

Иерархии классов

Это применимо только в том случае, если исключения разрабатываете вы. Если это не описывает этот случай, переходите к следующей главе.

Если у вас есть, скажем, API аутентификации пользователя, вы, вероятно, захотите создать целый набор исключений, таких как UserNotFoundException , IncorrectPasswordException , UserFrozenException , IpBlockedException и многое другое.

Если вы убедитесь, что все эти исключения расширяют один тип исключения ( AuthenticationException ), вот так:

 public class AuthenticationException extends Exception {
    public AuthenticationException(String msg, Throwable cause) {
        super(msg, cause);
    }

    public AuthenticationException(String msg) {
        super(msg);
    }
}

public class UserNotFoundException extends AuthenticationException {
    public UserNotFoundException(String username) {
        super(username);
    }
}

// and so on
  

тогда вызывающий может выбирать, вот почему это создает такой отличный дизайн API.

ЕСЛИ вызывающему абоненту необходимо иметь пользовательские реакции в зависимости от того, какая проблема с аутентификацией возникает, они могут перехватить, скажем, UserNotFoundException . Но если им все равно, они могут перехватить AuthenticationException .

Обратите внимание, что сама Java тоже это делает. GeneralSecurityException является общим суперклассом, и все же большинство методов, связанных с безопасностью, выдают значительный список подклассов этого, так что вызывающие пользователи точно знают, какие ошибки могут возникнуть, и могут писать обработчики для конкретных ошибок, не заставляя их писать тонну блоков catch, если они этого не хотят.

повторное перемещение

Альтернативное решение заключается в том, что вы объединяете исключения в свой собственный пользовательский тип.

Для этого сначала напишите исключение (или используйте существующее, но это редко бывает правильным со стилистической точки зрения). Затем напишите блок catch и перенесите все эти странные, не связанные исключения в это вновь созданное, убедившись, что используется система ’cause’:

 public class CustomException { // find a better name
    public CustomException(String msg, Throwable cause) {
        super(msg, cause);
    }

    public CustomException(String msg) {
        super(msg);
    }

    public CustomException(Throwable cause) {
        super(cause);
    }
}

public void myMethod() throws CustomException {
    try {
        // code here
    } catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
        throw new CustomException(e);
    } catch (InvocationTargetException e) {
        throw new CustomException(e.getCause();
    }
}
  

но обратите внимание, как ваше предложение о том, что вы просто хотите запустить и забыть, вызывает проблемы с дизайном вашего API. Например, обертывание InvocationTargetException подобным образом довольно враждебно по отношению к вашим потребителям API; InvocationTargetException само по себе является оболочкой, так что теперь у вас есть wrappers-in-wrappers-in-wrappers, и это просто уродливо, и вызывающему коду очень сложно даже пытаться решать проблемы точечным способом. На самом деле нет простого способа прочитать используемые вами API и понять, почему он выдает различные исключения, которые он выдает.

ПРИМЕЧАНИЕ: Если вы можете, решение с иерархией классов — гораздо лучший способ сделать это; но для этого требуется, чтобы вы контролировали код, который их генерирует.

Ответ №2:

Предположим, у вас есть:

 void myMethod() throws ApiException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
          /// Your code here;
}
  

Вы можете изменить его с помощью:

 void myMethod() throws CustomException {
       try {
          /// Your code here;
       } catch (ApiException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
         throw new CustomException(e);   // Make sure to have constructor initialized with cause.
       }
    }
  

Например, класс CustomException:

 public class CustomException {
   CustomException(Throwable cause) {
      super(cause);
   }
}
  

Ответ №3:

ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException являются ли все подклассы ReflectiveOperationException , поэтому вы можете просто уменьшить список исключений:

 throws ApiException, ReflectiveOperationException
  

Если вы хотите объединить эти 2 в CustomException , просто создайте одно и используйте его:

 public class CustomException extends Exception {
    private static final long serialVersionUID = -1006015659738896046L;

    public CustomException(Exception e) {
        super(e);
    }
    public CustomException(String msg, Exception e) {
        super(msg, e);
    }
}
  
 void foo() throws CustomException {
    try {
        // your code here
    } catch (ApiException | ReflectiveOperationException e) {
        throw new CustomException(e);
    }
}
  

Если вам даже не нужно throws CustomException , потому что вы знаете, что вызывающий объект никогда специально не поймает его, измените класс исключения на CustomException extends RuntimeException .