Собственные исключения
Зачем создавать свои исключения
В Java уже есть много готовых исключений, но иногда ошибка относится именно к правилам твоего приложения.
Например, вместо общего RuntimeException гораздо понятнее увидеть
UserNotFoundException: уже по имени ясно, что именно произошло.
Собственное исключение — это обычный класс, который наследуется от подходящего класса исключений.
Выбор между Exception и RuntimeException
подчиняется тем же правилам checked и unchecked исключений, которые мы уже разобрали.
Создаём собственное исключение
Чаще всего классу исключения не нужна сложная логика. Достаточно дать ему понятное имя и передать сообщение конструктору родительского класса.
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Теперь это исключение можно выбрасывать так же, как любое встроенное:
public User findUser(long id) {
User user = loadUser(id);
if (user == null) {
throw new UserNotFoundException("User not found: " + id);
}
return user;
}
Код, который вызывает findUser(), получает не абстрактную «ошибку времени выполнения»,
а конкретный тип ошибки. Его можно отдельно перехватить через catch,
если на эту ситуацию нужно отреагировать особым образом.
Checked или unchecked
Собственное исключение не становится checked или unchecked само по себе — это определяется его родителем.
Если класс наследуется от Exception, но не от RuntimeException,
компилятор требует обработать или объявить такое исключение. Наследник RuntimeException
остаётся unchecked.
public class InvalidReportException extends Exception {
public InvalidReportException(String message) {
super(message);
}
}
Поэтому сначала определяют смысл ошибки, а уже затем выбирают базовый класс. Наследование здесь влияет не только на место класса в иерархии, но и на то, как компилятор заставляет вызывающий код работать с этим исключением.
PaymentRejectedException или UserNotFoundException
обычно говорят о проблеме больше, чем безликий RuntimeException с тем же текстом сообщения.
Сообщение и причина ошибки
Если новое исключение создаётся поверх уже возникшей ошибки, полезно сохранить исходную причину.
Для этого можно добавить конструктор с параметром Throwable cause
и передать его в super.
public class UserLoadingException extends RuntimeException {
public UserLoadingException(String message, Throwable cause) {
super(message, cause);
}
}
Так у нового исключения остаётся собственный понятный смысл, но исходная ошибка не теряется:
её можно получить через getCause(), а цепочка причин сохраняется в stack trace.