Обработка причины ExecutionException

Предположим, у меня есть класс, определяющий большой блок работы, который может произвести несколько проверенных исключений.

class WorkerClass{
   public Output work(Input input) throws InvalidInputException, MiscalculationException {
      ...
   }
}

Теперь предположим, что у меня есть графический интерфейс, который может вызывать этот класс. Я использую SwingWorker для делегирования задачи.

Final Input input = getInput();
SwingWorker<Output, Void> worker = new SwingWorker<Output, Void>() {
        @Override
        protected Output doInBackground() throws Exception {
            return new WorkerClass().work(input);
        }
};

Как я могу обработать возможные исключения, выданные из SwingWorker? Я хочу провести различие между Исключениями моего рабочего класса (InvalidInputException и MiscalculationException), но оболочка ExecutionException усложняет ситуацию. Я только хочу обработать эти исключения - OutOfMemoryError не должен быть пойман.

try{
   worker.execute();
   worker.get();
} catch(InterruptedException e){
   //Not relevant
} catch(ExecutionException e){
   try{
      throw e.getCause(); //is a Throwable!
   } catch(InvalidInputException e){
      //error handling 1
   } catch(MiscalculationException e){
      //error handling 2
   }
}
//Problem: Since a Throwable is thrown, the compiler demands a corresponding catch clause.

3 ответа

Решение
catch (ExecutionException e) {
    Throwable ee = e.getCause ();

    if (ee instanceof InvalidInputException)
    {
        //error handling 1
    } else if (ee instanceof MiscalculationException e)
    {
        //error handling 2
    }
    else throw e; // Not ee here
}

Вы можете использовать некрасивый (умный?) Хак, чтобы преобразовать бросаемый объект в непроверенное исключение. Преимущество состоит в том, что вызывающий код будет получать любое исключение, которое было сгенерировано вашим рабочим потоком, независимо от того, отмечен он или нет, но вам не нужно изменять сигнатуру вашего метода.

try {
    future.get();
} catch (InterruptedException ex) {
} catch (ExecutionException ex) {
    if (ex.getCause() instanceof InvalidInputException) {
        //do your stuff
    } else {
        UncheckedThrower.throwUnchecked(ex.getCause());
    }
}

С UncheckedThrower определяется как:

class UncheckedThrower {

    public static <R> R throwUnchecked(Throwable t) {
        return UncheckedThrower.<RuntimeException, R>trhow0(t);
    }

    @SuppressWarnings("unchecked")
    private static <E extends Throwable, R> R trhow0(Throwable t) throws E {
        throw (E) t;
    }
}

Попробуйте / мульти-улов:

try {
    worker.execute();
    worker.get();
} catch (InterruptedException e) {
    //Not relevant
} catch (InvalidInputException e) {
    //stuff
} catch (MiscalculationException e) {
    //stuff
}

Или с ExecutionException обертка:

catch (ExecutionException e) {
    e = e.getCause();
    if (e.getClass() == InvalidInputException.class) {
        //stuff
    } else if (e.getClass() == MiscalculationException.class) {
        //stuff
    }
}

Или, если вы хотите, чтобы подклассы исключений рассматривались как их родители:

catch (ExecutionException e) {
    e = e.getCause();
    if (e instanceof InvalidInputException) {
        //stuff
    } else if (e instanceof MiscalculationException) {
        //stuff
    }
}
Другие вопросы по тегам