Можно ли определить, произошло ли исключение до того, как я вошел в блок finally? - PullRequest
21 голосов
/ 09 октября 2008

Есть ли в Java элегантный способ определить, произошло ли исключение перед выполнением блока finally? При работе с операторами close () обычно требуется обработка исключений в блоке finally. В идеале мы хотели бы сохранить оба исключения и распространять их (поскольку оба они могут содержать полезную информацию). Единственный способ сделать это - создать переменную вне области действия try-catch-finally для сохранения ссылки на выброшенное исключение. Затем распространите «сохраненное» исключение на любое, встречающееся в блоке finally.

Есть ли более элегантный способ сделать это? Возможно, вызов API, который покажет это?

Вот примерный код того, о чем я говорю:

Throwable t = null; 
try {   
   stream.write(buffer); 
} catch(IOException e) {
    t = e;   //Need to save this exception for finally
    throw e;
} finally {   
    try {
       stream.close();   //may throw exception
   } catch(IOException e) {
      //Is there something better than saving the exception from the exception block?
      if(t!=null) {
         //propagate the read exception as the "cause"--not great, but you see what I mean.
         throw new IOException("Could not close in finally block: " + e.getMessage(),t);
      } else {
         throw e;  //just pass it up
      }    
   }//end close
}

Очевидно, что есть ряд других похожих кладж, которые могут включать сохранение исключения как переменную-член, возврат его из метода и т. Д., Но я ищу что-то более элегантное.

Может быть, что-то вроде Thread.getPendingException() или что-то подобное? В таком случае, есть ли элегантное решение на других языках?

Этот вопрос фактически возник из комментариев в другом вопросе , который поднял интересный вопрос.

Ответы [ 5 ]

12 голосов
/ 09 октября 2008

Ваша идея об установке переменной вне области действия try / catch / finally верна.

Не может быть одновременно более одного исключения.

6 голосов
/ 30 августа 2009

Вместо использования логического флага я буду хранить ссылку на объект Exception. Таким образом, у вас есть не только способ проверить, произошло ли исключение (объект будет нулевым, если не возникло исключение), но вы также получите доступ к самому объекту исключения в вашем блоке finally, если исключение действительно произошло. Вам просто нужно помнить, чтобы установить объект ошибки во всех ваших блоках перехвата (если вы сбросите ошибку).

Я думаю, что это недостающая возможность языка C #, которую нужно добавить. Блок finally должен поддерживать ссылку на базовый класс Exception, аналогично тому, как его поддерживает блок catch, так что ссылка на распространяющееся исключение доступно для блока finally. Это будет простой задачей для компилятора , , сохраняющей нам работу из вручную создание локальной переменной Exception и запоминание для установки вручную его значение перед повторной выдачей ошибки, а также предотвращает совершение ошибки установки переменной Exception, когда повторная ошибка не выдается (помните, что мы хотим сделать видимыми только неперехваченные исключения блок finally).

finally (Exception main_exception)
{
    try
    {
        //cleanup that may throw an error (absolutely unpredictably)
    }
    catch (Exception err)
    {
        //Instead of throwing another error,
        //just add data to main exception mentioning that an error occurred in the finally block!
        main_exception.Data.Add( "finally_error", err );
        //main exception propagates from finally block normally, with additional data
    }
}

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

2 голосов
/ 10 октября 2008

Использовать логирование ...

try {   
   stream.write(buffer); 
} catch(IOException ex) {
    if (LOG.isErrorEnabled()) { // You can use log level whatever you want
        LOG.error("Something wrong: " + ex.getMessage(), ex);
    }
    throw ex;
} finally {   
    if (stream != null) {
        try {
            stream.close();
        } catch (IOException ex) {
            if (LOG.isWarnEnabled()) {
                LOG.warn("Could not close in finally block", ex);
            }
        }
    }
}
2 голосов
/ 09 октября 2008

Вы всегда можете установить логический флаг в вашем улове (ах). Я не знаю ни одного «хитрого» способа сделать это, но тогда я скорее парень .Net.

1 голос
/ 21 декабря 2010

В vb.net можно использовать оператор «Catch ... When» для захвата исключения в локальную переменную без его фактического перехвата. Это имеет ряд преимуществ. Среди них:

  1. Если ничто не собирается «в конечном счете» перехватить исключение, необработанная ловушка исключения будет запущена с места исходного исключения. Гораздо приятнее, чем ловушка отладчика при последнем повторном отбрасывании, особенно потому, что информация, которая может понадобиться для отладки, еще не вышла из сферы действия или не была подхвачена утверждениями «наконец».
  2. Несмотря на то, что rethrow не очистит трассировку стека, как это делает Throw Ex, он все равно будет часто сглаживать трассировку стека. Если исключение не перехвачено, трассировка стека будет чистой.

Поскольку эта функция не поддерживается в vb, может быть полезно написать обертку vb для реализации кода на C (например, с учетом MethodInvoker и Action (Of Exception), выполнить MethodInvoker с помощью «Try» и Action в "Наконец-то".

Одна интересная особенность: для Catch-When возможно увидеть исключение, которое в конечном итоге будет перезаписано исключением в конце предложения. В некоторых случаях это может быть хорошо; в других случаях это может сбивать с толку. В любом случае, это то, о чем нужно знать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...