Какой базовый тип исключения выбрать для случая, когда переданный объект не подходит для проверки в сервисе?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Выбор типа исключения для некорректных объектов
Вопрос о правильном выборе типа исключения — это вопрос о чистоте архитектуры и правильной обработке ошибок. Для случая, когда переданный объект не подходит для обработки в сервисе, есть несколько вариантов.
Правильный выбор: IllegalArgumentException
IllegalArgumentException — это оптимальный выбор в большинстве случаев, когда переданный аргумент не подходит для метода:
public class UserValidator {
public void validateUser(User user) {
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
if (user.getId() <= 0) {
throw new IllegalArgumentException("User ID must be positive");
}
if (user.getEmail() == null || user.getEmail().isEmpty()) {
throw new IllegalArgumentException("User email is required");
}
}
}
Иерархия исключений Java
Throwable
├── Exception
│ ├── RuntimeException (unchecked)
│ │ ├── IllegalArgumentException
│ │ ├── NullPointerException
│ │ ├── IllegalStateException
│ │ └── ...
│ └── IOException (checked)
│ └── ...
└── Error
├── OutOfMemoryError
└── StackOverflowError
Типы исключений и когда их использовать
1. IllegalArgumentException (РЕКОМЕНДУЕТСЯ)
Используй когда параметры метода некорректны:
public class PaymentService {
public void processPayment(BigDecimal amount, String currency) {
// Проверка параметров
if (amount == null || amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException(
"Amount must be greater than zero, got: " + amount
);
}
if (currency == null || currency.isEmpty()) {
throw new IllegalArgumentException("Currency is required");
}
if (!isValidCurrency(currency)) {
throw new IllegalArgumentException(
"Invalid currency: " + currency
);
}
// Обработка платежа
}
}
2. NullPointerException (только в edge cases)
Травня генерировать явно редко, но иногда полезно для debug-а:
public void processData(Object obj) {
if (obj == null) {
// Не рекомендуется
throw new NullPointerException("Object cannot be null");
// Лучше
throw new IllegalArgumentException("Object cannot be null");
}
}
3. IllegalStateException
Используй когда объект находится в неправильном состоянии, не для параметров:
public class Order {
private OrderStatus status = OrderStatus.PENDING;
public void cancelOrder() {
if (status == OrderStatus.SHIPPED) {
throw new IllegalStateException(
"Cannot cancel order that is already shipped"
);
}
this.status = OrderStatus.CANCELLED;
}
}
4. ClassCastException
Используй когда неправильный тип данных:
public void processObject(Object obj) {
if (!(obj instanceof User)) {
throw new ClassCastException(
"Expected User, but got " + obj.getClass().getName()
);
}
User user = (User) obj;
}
Практический пример: валидация в сервисе
@Service
public class UserService {
/**
* Создаёт нового пользователя
* @param user объект пользователя
* @throws IllegalArgumentException если user имеет некорректные данные
*/
public User createUser(User user) {
// Проверка объекта
if (user == null) {
throw new IllegalArgumentException("User object cannot be null");
}
// Проверка полей
if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
throw new IllegalArgumentException("Email is required");
}
if (!isValidEmail(user.getEmail())) {
throw new IllegalArgumentException(
"Invalid email format: " + user.getEmail()
);
}
if (user.getAge() < 0 || user.getAge() > 150) {
throw new IllegalArgumentException(
"Age must be between 0 and 150, got: " + user.getAge()
);
}
// Проверка состояния
if (userExists(user.getEmail())) {
throw new IllegalStateException(
"User with email " + user.getEmail() + " already exists"
);
}
// Сохранение
return userRepository.save(user);
}
}
Кастомные исключения (расширенный подход)
Для более сложных сценариев можно создавать кастомные исключения:
// Кастомное исключение для бизнес-логики
public class InvalidUserException extends IllegalArgumentException {
public InvalidUserException(String message) {
super(message);
}
public InvalidUserException(String message, Throwable cause) {
super(message, cause);
}
}
// Использование
public class UserValidator {
public void validate(User user) throws InvalidUserException {
if (user == null) {
throw new InvalidUserException("User cannot be null");
}
// ... другие проверки
}
}
Правила выбора исключения
| Ситуация | Исключение | Пример |
|---|---|---|
| Параметр null или некорректен | IllegalArgumentException | amount <= 0 |
| Неправильный тип объекта | ClassCastException | ожидается User, получен String |
| Объект в неправильном состоянии | IllegalStateException | попытка отменить уже отправленный заказ |
| Логическая ошибка (не ожидаемое) | IllegalStateException | не инициализирована нужная переменная |
| Операция невозможна | UnsupportedOperationException | метод не реализован |
Лучшие практики
1. Выбирай наиболее специфичное исключение
// ❌ Слишком общее
throw new Exception("User validation failed");
// ✅ Специфичное
throw new IllegalArgumentException("User email is invalid: " + email);
2. Добавляй описательное сообщение об ошибке
// ❌ Неинформативно
throw new IllegalArgumentException("Invalid user");
// ✅ Информативно
throw new IllegalArgumentException(
"User email must not be empty, got: '" + email + "'"
);
3. Включай контекст ошибки
throw new IllegalArgumentException(
String.format(
"User age must be between %d and %d, got %d",
MIN_AGE, MAX_AGE, age
)
);
4. Проверяй параметры в начале метода
public void processUser(User user) {
// Проверки в начале
if (user == null) {
throw new IllegalArgumentException("User cannot be null");
}
// Логика после проверок
// ...
}
Заключение
Для большинства случаев, когда переданный объект не подходит для проверки в сервисе, используй IllegalArgumentException. Это стандартное исключение, которое явно говорит что проблема в параметрах метода. Для более специфичных случаев (состояние объекта, тип данных) выбирай соответствующее исключение из иерархии RuntimeException.