Как сделать так, чтобы исключения не теряли информацию о трассировке стека в Java? - PullRequest
0 голосов
/ 19 сентября 2019

Недавно я столкнулся с некоторой проблемой исключений в java, которая напомнила мне типичную идиому, рекомендованную Брюсом Экелом :

Преобразование отмеченных исключений в исключение

Настоящая проблема в том, что когда вы пишете тело обычного метода, и вы вызываете другой метод и понимаете: «Я понятия не имею, что здесь делать с этим исключением, но я не хочу проглотить его или напечатать какое-нибудь банальное сообщение."За исключением цепочек, новое и простое решение предотвращает себя.Вы просто «оборачиваете» проверенное исключение внутри RuntimeException , передавая его конструктору RuntimeException , например:

try {
  // ... to do something useful
} catch (IDontKnowWhatToDoWithThisCheckedException e) {
  throw new RuntimeException(e);
}

Это кажется идеальным решениемесли вы хотите «отключить проверенное исключение - вы его не проглотите, и вам не нужно включать его в спецификацию исключений вашего метода, но из-за цепочки исключений вы не потеряете никакой информации из исходного исключения.

Этот метод предоставляет возможность игнорировать исключение и позволить ему пузыриться в стеке вызовов без необходимости писать предложения try-catch и / или спецификации исключений.

Однако яобнаружил, что в некоторых случаях это не сработало, как показано здесь:

package exceptions;
// How an exception can be lost
class VeryImportantException extends Exception {
    @Override
    public String toString() {
        return "A very important exception";
    }
}

class HoHumException extends Exception {
    @Override
    public String toString() {
        return "A trivial exception";
    }
}

public class LostMessage {
    void f() throws VeryImportantException {
        throw new VeryImportantException();
    }

    void dispose() throws HoHumException {
        throw new HoHumException();
    }
    public static void main(String[] args) {
        try {
            LostMessage lm = new LostMessage();
            try {
                lm.f();
            } catch (VeryImportantException e) {
                throw new RuntimeException(e);
            } finally {
               lm.dispose();
            } 
        } catch (Exception e) {
            throw new RuntimeException(e);
        }        
    }
}/* Output:
Exception in thread "main" java.lang.RuntimeException: A trivial exception
    at exceptions.LostMessage.main(LostMessage.java:36)
Caused by: A trivial exception
    at exceptions.LostMessage.dispose(LostMessage.java:23)
    at exceptions.LostMessage.main(LostMessage.java:33) 
*///:~

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

Джошуа Блох рекомендует try-with-resource способ, которым ресурс должен реализовать интерфейс AutoCloseable , который процесс несколько сложен.

Итак, мой вопрос таков: есть ли способ, которым я могу использовать, чтобы исключение не потеряло информацию о трассировке стека при подходе Брюса Экеля ?

Ответы [ 2 ]

1 голос
/ 19 сентября 2019

Возможно, вы захотите использовать try-with-resource вместо finally блока.Это имеет тенденцию обрабатывать эту ситуацию, как будто вы хотите, чтобы ситуация была обработана.См. эту статью для более подробной информации.

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

1 голос
/ 19 сентября 2019

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

Один из простых способов избежать этого - не выбрасывать исключение изБлок наконец.

Например:

try {
    LostMessage lm = new LostMessage();
    try {
        lm.f();
    } catch (VeryImportantException e) {
       throw new RuntimeException(e);
    } finally {
        try {
           lm.dispose();
        } catch ( HoHumException e ) {
           // No-op or logging
           //
           // If we're exiting this try-finally because an exception
           // was thrown, then don't allow this new exception to replace it.
        }
    } 
} catch (Exception e) {
    throw new RuntimeException(e);
}        
...