Питання 2 · Розділ 7

Що таке checked виняток і коли його використовувати?

Використовуйте checked exception якщо:

Мовні версії: English Russian Ukrainian

Junior Level

Визначення

Checked exception (перевірюваний виняток) — це тип винятку, який компілятор Java зобов’язує обробити.

Навіщо: Java-дизайнери хотіли змусити розробника явно вирішити, як реагувати на передбачувані зовнішні збої – файл може бути відсутнім, мережа може зникнути. Без checked exceptions ці ситуації легко пропустити. Якщо метод може викинути checked виняток, ви повинні:

  1. Огорнути виклик у try-catch
  2. Або додати throws у сигнатуру метода

Приклади

// Компілятор змусить обробити IOException
public String readFile() throws IOException {
    return Files.readString(Paths.get("data.txt"));
}

// Обов'язково обробити
public void safeRead() {
    try {
        Files.readString(Paths.get("data.txt"));
    } catch (IOException e) {
        System.err.println("Failed to read: " + e.getMessage());
    }
}

Коли використовувати

Використовуйте checked exception якщо:

  • Збій очікуваний — файл може бути відсутнім, мережа може зникнути
  • Збій відновлюваний — можна повторити спробу, спробувати інший шлях
  • Це критично — не можна просто “забути” про цю ситуацію

Поширені checked винятки

  • IOException — проблеми введення-виведення
  • SQLException — помилки бази даних
  • ClassNotFoundException — клас не знайдено
  • InterruptedException — потік перервано

Middle Level

Що обрати: throws чи try-catch?

  • throws – коли метод не може прийняти рішення про обробку. Нехай викликач вирішує.
  • try-catch – коли можете обробити на місці: логувати, повернути дефолтне значення, повторити запит.

Механізм компіляції

Компілятор javac перевіряє граф викликів. Якщо десь у ланцюжку викликається метод з checked винятком і він не оброблений, компіляція перерветься.

Exception Translation

У великих проектах з багатошаровою архітектурою проброс checked винятків порушує інкапсуляцію шарів. У невеликих проектах допустимо пробрасувати їх наверх.

public Order createOrder(Order order) {
    try {
        return orderRepository.save(order);
    } catch (SQLException e) {
        throw new OrderStorageException("Failed to save order", e);
    }
}

OrderStorageException — це runtime виняток. Бізнес-логіка не залежить від JDBC.

Три умови використання checked exceptions

  1. Збій очікуваний — не баг у коді, а зовнішня обставина
  2. Збій відновлюваний — код викликача може щось зробити
  3. Критично для бізнесу — не можна проігнорувати

Lambda та checked exceptions

Функціональні інтерфейси не підтримують checked винятки:

// Не скомпілюється!
list.stream().map(path -> Files.readString(path))
// Чому: інтерфейс Function<T,R> не оголошує throws у сигнатурі apply().
// Тому лямбда не може пробросити checked виняток -- компілятор не пропустить.

// Рішення — обгортка
list.stream().map(path -> {
    try {
        return Files.readString(path);
    } catch (IOException e) {
        throw new UncheckedIOException(e);
    }
})

Senior Level

Under the Hood: атрибут Exceptions

У .class файлі інформація про throws зберігається в атрибуті метода Exceptions. JVM не використовує його для контролю у рантаймі — лише для верифікатора компілятора.

Binary Compatibility

Додавання checked винятку у метод існуючої бібліотеки ламає бінарну сумісність. Усі клієнти перестануть компілюватися.

Видалення винятку — бінарно сумісно, але залишає зайвий код у клієнтів.

Overriding та LSP

Принцип підстановки Лісков диктує правила:

  • Метод-нащадок може не викидати винятки батька
  • Може викидати підтипи винятків батька
  • НЕ може викидати нові checked винятки

Продуктивність

Checked виняток — це все ще Throwable з дорогим fillInStackTrace(). Для частих “очікуваних” ситуацій краще повертати Optional<T> або Result-об’єкти.

Edge Cases

  • InterruptedException — “хороший” checked виняток. При перехопленні відновлюйте статус: Thread.currentThread().interrupt()
  • Generic Throws — Java дозволяє public <E extends Exception> void doWork() throws E
  • throws Exception — ніколи не пишіть так у публічному API

Моніторинг

Не кидайте Exception або Throwable. Створюйте свої типи для чіткого розділення помилок у Micrometer/Prometheus.


🎯 Шпаргалка для співбесіди

Обов’язково знати:

  • Checked exception — компілятор зобов’язує обробити (try-catch або throws)
  • Використовувати, якщо збій очікуваний, відновлюваний і критичний для бізнесу
  • Поширені: IOException, SQLException, ClassNotFoundException, InterruptedException
  • throws — коли метод не може вирішити, як обробити; try-catch — коли можете
  • Exception Translation — загортання checked у доменний unchecked для шарів архітектури
  • Lambda не підтримує checked винятки — потрібна обгортка в UncheckedIOException
  • Додавання checked винятку ламає бінарну сумісність бібліотеки
  • При overriding метод-нащадок НЕ може додавати нові checked винятки (LSP)

Часті уточнюючі запитання:

  • Чому checked винятки не працюють з лямбдами? — Функціональні інтерфейси (Function, Predicate) не оголошують throws у сигнатурі
  • Що буде при додаванні checked винятку у публічний API бібліотеки? — Усі клієнти перестануть компілюватися — ламає бінарну сумісність
  • Як правильно обробити InterruptedException? — Перехопити і відновити статус: Thread.currentThread().interrupt()
  • Коли checked виняток кращий за unchecked? — Коли викликач може щось зробити (повторити, обрати альтернативний шлях)

Червоні прапорці (НЕ говорити):

  • “Я пишу throws Exception скрізь, щоб не думати” — це ламає контракт метода
  • “Checked exceptions — це проблема JVM, а не додатку” — ні, це помилки додатку
  • “Я перехоплюю усі checked винятки і нічого не роблю” — мовчазне проковтування помилок

Пов’язані теми:

  • [[В чому різниця між checked та unchecked винятками]]
  • [[Що таке unchecked виняток (Runtime Exception)]]
  • [[Які винятки потрібно обробляти обов’язково]]
  • [[Що знаходиться на вершині ієрархії винятків]]
  • [[Що таке Throwable]]