Синтаксис Java 7 try-with-resources (также известный как блок ARM (автоматическое управление ресурсами)) хорош, короток и прост при использовании только одного ресурса AutoCloseable
. Однако я не уверен, что является правильной идиомой, когда мне нужно объявить несколько ресурсов, которые зависят друг от друга, например, FileWriter
и BufferedWriter
, которые его обертывают. Конечно, этот вопрос касается любого случая, когда некоторые ресурсы AutoCloseable
обернуты не только этими двумя конкретными классами.
Я придумал три следующих альтернативы:
1)
Наивная идиома, которую я видел, заключается в объявлении только обертки верхнего уровня в переменной, управляемой ARM:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Это хорошо и коротко, но оно сломано. Поскольку базовый FileWriter
не объявляется в переменной, он никогда не будет закрыт непосредственно в сгенерированном блоке finally
. Он будет закрыт только с помощью метода close
обертывания BufferedWriter
. Проблема заключается в том, что если исключение выбрано из конструктора bw
, его close
не будет вызываться, и поэтому базовый FileWriter
не будет закрыт.
2)
static void printToFile2(String text, File file) {
try (FileWriter fw = new FileWriter(file);
BufferedWriter bw = new BufferedWriter(fw)) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Здесь как базовый, так и оберточный ресурс объявляются в управляемых ARM переменных, поэтому оба они обязательно будут закрыты, но базовый fw.close()
будет вызываться дважды: не только напрямую, но также через обертку bw.close()
.
Это не должно быть проблемой для этих двух конкретных классов, которые реализуют Closeable
(который является подтипом AutoCloseable
), контракт которого утверждает, что разрешено несколько вызовов close
:
Закрывает этот поток и освобождает связанные с ним системные ресурсы. Если поток уже закрыт, вызов этого метода не имеет эффекта.
Однако в общем случае у меня могут быть ресурсы, которые реализуют только AutoCloseable
(а не Closeable
), что не гарантирует, что close
можно вызвать несколько раз:
Обратите внимание, что в отличие от метода close из java.io.Closeable этот метод закрытия не требуется идемпотентным. Другими словами, вызов этого закрытого метода несколько раз может иметь некоторый видимый побочный эффект, в отличие от Closeable.close, который не должен иметь эффекта, если вызывается более одного раза. Однако разработчикам этого интерфейса настоятельно рекомендуется использовать свои близкие методы идемпотентными.
3)
static void printToFile3(String text, File file) {
try (FileWriter fw = new FileWriter(file)) {
BufferedWriter bw = new BufferedWriter(fw);
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
Эта версия должна быть теоретически правильной, поскольку только fw
представляет собой реальный ресурс, который необходимо очистить. bw
сам не владеет каким-либо ресурсом, он делегирует только fw
, поэтому его должно быть достаточно, чтобы закрыть только базовый fw
.
С другой стороны, синтаксис является немного нерегулярным, а также Eclipse выдает предупреждение, которое, по моему мнению, является ложным сигналом тревоги, но это все еще предупреждение, с которым приходится иметь дело:
утечка ресурса: "bw" никогда не закрывается
Итак, к какому подходу нужно идти? Или я пропустил какую-то другую идиому, которая является правильной?