Как окружить код с помощью try / catch наиболее элегантно - PullRequest
2 голосов
/ 15 июня 2009

У меня часто возникают проблемы при использовании try-and-catch:

1) Некоторые переменные должны быть объявлены в скобках try, иначе они не будут находиться в области видимости
2) В конечном счете, даже мое возвращаемое утверждение заканчивается тем, что он должен быть в скобке попытки, но тогда метод ничего не возвращает.

Как правильно обойти эту проблему.

Пример метода, вызывающего эту проблему, приведен ниже. Для этого необходимо обработать FileNotFoundException и IOException. Как мне сделать это наиболее элегантно?

public static String getContents (File file) {
      BufferedReader reader = new BufferedReader(new FileReader(file));
      String contents = new String();
      while (reader.ready())
        contents += reader.readLine();
      return contents;
    }

Ответы [ 8 ]

10 голосов
/ 15 июня 2009

Существует еще одна опция, если обработка метода исключений не требуется в методе getContents - добавьте в метод предложение throws, чтобы метод вызывал исключение:

public static String getContents (File file)
    throws IOException, FileNotFoundException {

Таким образом, код, вызывающий метод, будет обрабатывать Exception s, а не сам метод. В этом методе не будет необходимости в try / catch блоках, если Exception s выбрасывается в методы, которые его вызвали.

Это может или не может быть желательным способом справиться с ситуацией, в зависимости от того, как метод должен вести себя.

Редактировать

Подумав, метод может вызвать исключение. Я думаю, что комментарий Д. Шоули, я думаю, хорошо подытоживает его: «обработка исключений должна означать обработку исключений только там, где это имеет смысл».

В этом случае появляется метод getContents, который получает содержимое указанного File и возвращает вызывающему String.

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

Однако, если сам метод возвращает исключение вызывающей стороне, вызывающая сторона может выбрать соответствующую реакцию:

try {
    String contents = getContents(new File("input.file"));
} catch (IOException ioe) {
    // Perform exception handling for IOException.
} catch (FileNotFoundException fnfe) {
    // Inform user that file was not found.
    // Perhaps prompt the user for an alternate file name and try again?
}

Вместо того, чтобы метод setContents придумал собственный протокол для уведомления о том, что произошла ошибка, вероятно, было бы лучше выбросить IOException и FileNotFoundException обратно в вызывающую функцию, чтобы обработка исключений могла выполняться в месте, где могут выполняться соответствующие альтернативные действия.

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

5 голосов
/ 15 июня 2009

Вы можете справиться с этим следующим образом:

StringBuilder contents = new StringBuilder();
BufferedReader reader;

try {
   reader = new BufferedReader(new FileReader(file));

   while (reader.ready()) {
      contents.append(reader.readLine());
   }

} catch (FileNotFoundException fne) {
   log.warn("File Not Found", fne);
} catch (IOException ioe) {
   log.warn("IOException", ioe);
} 

return contents.toString();

Вы, вероятно, должны использовать StringBuilder в приведенном выше случае вместо String, хотя, намного лучше с точки зрения производительности.

3 голосов
/ 15 июня 2009
public static String getContents (File file) {
    String contents = new String();
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(file));
        while (reader.ready())
            contents += reader.readLine();
    }
    catch (FileNotFoundException ex) {
        // handle FileNotFoundException
    }
    catch (IOException ex) {
        // handle IOException
    }
    finally {
        if (reader != null) {
            try {
                reader.close();
            }
            catch (IOException ex) {
                // handle IOException
            }
        }
    }
    return contents;
}

Я добавил блок finally, чтобы закрыть BufferedReader, хотя вы не сделали этого в своем коде. Я бы также предложил вам использовать StringBuilder вместо String конкатенации, но кто-то уже указал на это. Объявление и инициализация reader находятся за пределами блока try только из-за добавленного мной блока finally; в противном случае ссылка reader может быть объявлена ​​внутри блока try.

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

1 голос
/ 16 июня 2009

Здесь вам не хватает простой утилиты, которая значительно упрощает очистку:

public static void closeAndLog(Closable c) {
    if ( c == null )
        return;

    try { 
        c.close() 
    } catch ( IOException e) {
        LOGGER.warn("Failed closing " + c +, e);
    }
}

Таким образом, ваш код может стать:

public static String getContents (File file) throws IOException {

    BufferedReader r = null;

    try { 
        r = new BufferedReader(...);
        // do stuff
    } finally {
        closeAndLog(r);
    }
}
1 голос
/ 15 июня 2009

Что касается области видимости переменной, я не уверен, есть ли более элегантный способ сделать это. Обычно я пытаюсь придумать, какое у меня возвращаемое значение в случае ошибки, а затем назначаю переменную этому значению.

Что касается оператора return, то если вы воспользуетесь моим предложением выше, вы можете просто вернуться после блока try / catch.

Так что, если бы я использовал нулевое возвращаемое значение для обозначения ошибки, я бы сделал

public static String getContents (File file) {
    String contents = null;
    try {        
        BufferedReader reader = new BufferedReader(new FileReader(file));
        contents = new String();
        while (reader.ready())
            contents += reader.readLine();
    } catch (Exception e) {
        // Error Handling
    }
    return contents;
}
1 голос
/ 15 июня 2009

Вы можете попытаться переместить оператор возврата в блок finally.

0 голосов
/ 30 сентября 2015

Начиная с Java 7, вы можете использовать try-with-resources , чтобы убедиться, что ваши ресурсы закрыты правильно и «автоматически». Все, что вам нужно, это объект, который реализует java.lang.AutoCloseable, что делает BufferedReader. На самом деле в документах есть следующий пример:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
    return br.readLine();
}
0 голосов
/ 16 июня 2009

ИМХО, у вас есть два способа правильно обработать исключения (здесь IOException и FileNotFoundException):

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

Чтобы получить полезный совет по поводу исключений, см. Также: Почему Java-пользователи часто молча используют исключения?

...