Чи є спосіб зробити так, щоб runnable run () видавав виняток?


80

Метод, який я викликаю в run () у класі, що реалізує Runnable ), призначений для створення винятку.

Але компілятор Java не дозволяє мені цього робити, і пропонує, щоб я оточив його за допомогою try / catch.

Проблема в тому, що, оточуючи його за допомогою try / catch, я роблю цей конкретний запуск () марним. Я дійсно хочу , щоб кинути це виняток.

Якщо я вказав throwsдля самого run () , компілятор скаржиться на це Exception is not compatible with throws clause in Runnable.run().

Зазвичай я абсолютно в порядку, якщо не дозволяю run () видавати виняток. Але у мене є унікальна ситуація, коли я повинен мати таку функціональність.

Як мені обійти це обмеження?


Окрім інших відповідей, для відстеження прогресу завдання ви можете використовувати клас FutureTask.
JProgrammer

Відповіді:


26

Якщо ви хочете пройти клас, який реалізує, Runnableу Threadфреймворк, то вам доведеться грати за правилами цього фреймворку, див. Відповідь Ернеста Фрідмана-Хілла, чому робити це інакше - погана ідея.

Однак я гадаю, що ви хочете викликати runметод безпосередньо у своєму коді, тому ваш телефонний код може обробити виняток.

Відповідь на цю проблему проста. Не використовуйте Runnableінтерфейс з бібліотеки Thread, а натомість створіть власний інтерфейс із модифікованим підписом, який дозволяє кидати перевірене виключення, наприклад

public interface MyRunnable
{
    void myRun ( ) throws MyException;
}

Ви навіть можете створити адаптер, який перетворює цей інтерфейс на справжній Runnable(обробляючи перевірений виняток), придатний для використання в фреймворці Thread.


До такого простого рішення прийшло просто не думаючи "в коробці". Звичайно, a Runnable- це просто простий інтерфейс, і ми можемо зробити свій власний. Це не корисно для випадку використання потоку, але для передачі різних "запущених" фрагментів коду навколо це ідеально.
Річард Ле Мес'єр'є

82

Ви можете використовувати Callableзамість цього, надіславши його в файл ExecutorServiceі чекаючи результату з FutureTask.isDone()поверненим ExecutorService.submit().

Коли isDone()повертається істина, ви телефонуєте FutureTask.get(). Тепер, якщо ваш Callableвикинув, Exceptionтоді FutureTask.get()викине Exceptionтеж, і оригінальний виняток ви зможете отримати за допомогою Exception.getCause().


23

Якби run()кинув перевірений виняток, що б його вловити? Ви не можете вкласти цей run()виклик у обробник, оскільки ви не пишете код, який його викликає.

Ви можете run()зафіксувати перевірений виняток у методі та замінити невстановлений виняток (тобто RuntimeException) на його місце. Це завершить потік трасуванням стека; можливо, це те, що ви шукаєте.

Якщо замість цього ви хочете, щоб ваш run()метод десь повідомляв про помилку, ви можете просто надати метод зворотного виклику для виклику блоку run()методу catch; цей метод міг би десь зберігати об’єкт винятку, а потім зацікавлений потік міг знайти об’єкт у цьому місці.


2
Перша частина - невдалий аргумент. "Якби main()викинув перевірений виняток, що б його вловило?" "Якби run()викинув неперевірений виняток, що б його схопило?"
Christian Hujer

17

Так, є спосіб викинути перевірений виняток із run()методу, але це так жахливо, що я не буду ним ділитися.

Ось що ви можете зробити замість цього; він використовує той самий механізм, який застосовував би виняток виконання:

@Override
public void run() {
  try {
    /* Do your thing. */
    ...
  } catch (Exception ex) {
    Thread t = Thread.currentThread();
    t.getUncaughtExceptionHandler().uncaughtException(t, ex);
  }
}

Як зазначали інші, якщо ваш run()метод справді є ціллю a Thread, немає сенсу створювати виняток, оскільки він не спостерігається; кидання винятку має той самий ефект, що і не викидання винятку (жодного).

Якщо це не Threadціль, не використовуйте Runnable. Наприклад, можливо Callable, це краще підходить.


Але чи це спричиняє збій процесу, коли він кидає ??
Дінеш

@DineshVG Ні, лише помилка в JVM може спричинити справжній збій. Обробник винятків за замовчуванням просто друкує виняток. Якщо ви звикли бачити вихід процесу після цього, це тому, що цей потік був єдиним запущеним потоком, і він завершився.
erickson

Я спробував (у випадках тестування приладів Android) використовувати це для виклику мила, де, якщо я отримую 400 із виклику Soap, я вилучаю виняток. Цей виклик мила викликається з потоку під час запуску тесту. Цей потік використовує це, t.getUncaughtExceptionHandler().uncaughtException(t, ex);щоб перекинути його до тестування приладів. Додавання цього одного рядка призводить до аварійного завершення процесу !. Не знаю чому.
Дінеш

@DineshVG У цьому середовищі, робить Thread.getDefaultUncaughtExceptionHandler()повернення null? Якщо ні, то який тип результату? Що станеться, якщо замість того, щоб зателефонувати uncaughtException(), ви обернете перевірений виняток у a RuntimeExceptionі викинете його?
erickson

1
@DineshVG Android, можливо, встановлює за замовчуванням обробник винятків, який не спіймав, який це робить. Ось чому я запитав, чи Thread.getDefaultUncaughtExceptionHandler()повертається null; якщо ні, Android пропонує за замовчуванням, щоб пропонувати звіти тощо. Але ви можете налаштувати його робити те, що ви хочете. Тут є більше інформації .
erickson

6
@FunctionalInterface
public interface CheckedRunnable<E extends Exception> extends Runnable {

    @Override
    default void run() throws RuntimeException {
        try {
            runThrows();
        }
        catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    void runThrows() throws E;

}

1

Деякі люди намагаються переконати вас, що вам доведеться грати за правилами. Слухай, але чи ти слухаєшся, ти повинен вирішити сам залежно від твоєї ситуації. Реальність така: "ТРЕБА грати за правилами" (а не "Ви ПОВИННІ грати за правилами"). Тільки майте на увазі, що якщо ви не будете грати за правилами, це може мати наслідки.

Ситуація застосовується не тільки в ситуації Runnable, але і з Java 8 також дуже часто в контексті потоків та інших місць, де були введені функціональні інтерфейси без можливості мати справу з перевіреними винятками. Наприклад, Consumer, Supplier, Function, BiFunctionі так далі все було оголошено без засобів для боротьби з перевіряються винятками.

То які ситуації та варіанти? У наведеному нижче тексті Runnableпредставлено будь-який функціональний інтерфейс, який не оголошує винятки або оголошує винятки, занадто обмежені для конкретного випадку використання.

  1. Ви Runnableдесь самі заявили , і можете замінити Runnableчимось іншим.
    1. Подумайте про заміну Runnableна Callable<Void>. В основному те саме, але дозволено кидати винятки; і return nullзрештою повинен , що є легким роздратуванням.
    2. Подумайте про заміну Runnableвашим власним звичаєм, @FunctionalInterfaceякий може створити саме ті винятки, які ви хочете.
  2. Ви використовували API, і доступні альтернативи. Наприклад, деякі Java API перевантажені, тому ви можете використовувати їх Callable<Void>замість Runnable.
  3. Ви використовували API, і альтернатив немає. У цьому випадку у вас все ще немає можливостей.
    1. Ви можете обернути виняток RuntimeException.
    2. Ви можете зламати виняток у RuntimeException, використовуючи непомічений привід.

Ви можете спробувати наступне. Це трохи зламати, але іноді хак - це те, що нам потрібно. Тому що, чи слід перевіряти чи не перевіряти виняток, це визначається типом, але фактично має визначатися ситуацією.

@FunctionalInterface
public interface ThrowingRunnable extends Runnable {
    @Override
    default void run() {
        try {
            tryRun();
        } catch (final Throwable t) {
            throwUnchecked(t);
        }
    }

    private static <E extends RuntimeException> void throwUnchecked(Throwable t) {
        throw (E) t;
    }

    void tryRun() throws Throwable;
}

Я віддаю перевагу цьому, new RuntimeException(t)оскільки він має коротший слід стека.

Тепер ви можете зробити:

executorService.submit((ThrowingRunnable) () -> {throw new Exception()});

Застереження. Можливість виконувати неперевірені закиди таким чином може бути фактично вилучена в майбутніх версіях Java, коли інформація про загальний тип обробляється не тільки під час компіляції, але й під час виконання.


0

Ваша вимога не має жодного сенсу. Якщо ви хочете повідомити викликаного з потоку про виняток, що стався, ви можете зробити це за допомогою механізму зворотного виклику. Це може бути через обробник або трансляцію, або що завгодно інше, що ви можете подумати.


0

Я думаю, що шаблон слухача може допомогти вам у цьому сценарії. У випадку, якщо у вашому run()методі трапляється виняток , використовуйте блок try-catch, а в catch надішліть повідомлення про подію винятку. А потім обробити подію сповіщення. Я думаю, що це був би більш чистий підхід. Це посилання SO дає вам корисний вказівник на цей напрямок.


-1

Найпростіший спосіб - визначити власний об’єкт виключення, який розширює RuntimeExceptionклас замість Exceptionкласу.


І як би ви здобули цей RuntimeException, коли він трапляється, шановний сер?
Соня

Це одне не відповідає на питання.
Карл Ріхтер,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.