В реальных приложениях операции иногда падают временно: сеть моргнула, сервис ответил 503, база не успела принять соединение. В таких случаях полезно повторить действие несколько раз, а не сразу завершать сценарий ошибкой.
Опишем функциональный интерфейс для операции:
@FunctionalInterface
interface ThrowingSupplier<T> {
T get() throws Exception;
}
Теперь зададим количество попыток:
int attempts = 3;
И задержку между ними:
Duration delay = Duration.ofMillis(300);
Базовая идея такая:
try {
return action.get();
} catch (Exception e) {
Thread.sleep(delay.toMillis());
}Но нужно сохранить последнюю ошибку, если все попытки закончились неудачей:
Exception lastError = null;
Соберём retry в метод:
static <T> T retry(
ThrowingSupplier<T> action,
int attempts,
Duration delay
) throws Exception {
Exception lastError = null;
for (int i = 1; i <= attempts; i++) {
try {
return action.get();
} catch (Exception e) {
lastError = e;
if (i < attempts) {
Thread.sleep(delay.toMillis());
}
}
}
throw lastError;
}
Теперь можно обернуть нестабильную операцию:
String response = retry(
() -> loadFromRemoteApi(),
3,
Duration.ofMillis(300)
);
Если операция сработает со второй или третьей попытки, код продолжит выполнение. Если нет, наружу уйдёт последняя ошибка.
В production-коде к retry часто добавляют экспоненциальную задержку, логирование и ограничение по типам исключений.
👉 Java Ready | #практика