Poprawny idiom do zarządzania wieloma łańcuchami zasobów w bloku try-with-resources?
Java 7spróbuj z zasobami składnia (znana również jako blok ARM (Automatyczne zarządzanie zasobami)) jest miły, krótki i prosty, gdy używa tylko jednegoAutoCloseable
ratunek. Nie jestem jednak pewien, jaki jest prawidłowy idiom, gdy muszę zadeklarować wiele zasobów, które są od siebie zależne, na przykładFileWriter
i aBufferedWriter
to otacza to. Oczywiście to pytanie dotyczy każdego przypadku, gdy jest jakiśAutoCloseable
zasoby są zapakowane, a nie tylko te dwie konkretne klasy.
Wymyśliłem trzy następujące alternatywy:
1)Naiwny idiom, który widziałem, to zadeklarowanie tylko opakowania najwyższego poziomu w zmiennej zarządzanej przez ARM:
static void printToFile1(String text, File file) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
bw.write(text);
} catch (IOException ex) {
// handle ex
}
}
To jest ładne i krótkie, ale jest zepsute. Ponieważ podstawaFileWriter
nie jest zadeklarowany w zmiennej, nigdy nie zostanie zamknięty bezpośrednio w wygenerowanymfinally
blok. Będzie zamknięty tylko przezclose
metoda owijaniaBufferedWriter
. Problem polega na tym, że jeśli wyjątek zostanie odrzucony zbw
konstruktor, jegoclose
nie zostanie wywołany, a więc instrument bazowyFileWriter
nie zostanie zamknięty.
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
}
}
W tym przypadku zarówno zasoby bazowe, jak i opakowujące są zadeklarowane w zmiennych zarządzanych przez ARM, więc obie z pewnością będą zamknięte, ale podstawowafw.close()
zostanie wywołany dwukrotnie: nie tylko bezpośrednio, ale także poprzez opakowaniebw.close()
.
Nie powinno to stanowić problemu dla tych dwóch konkretnych klas, które implementująCloseable
(który jest podtypemAutoCloseable
), którego umowa stanowi, że wiele wywołańclose
są dozwolone:
Zamyka ten strumień i zwalnia wszystkie zasoby systemowe z nim związane. Jeśli strumień jest już zamknięty, wywołanie tej metody nie ma wpływu.
Jednak w ogólnym przypadku mogę mieć tylko zasoby, które implementujęAutoCloseable
(i nieCloseable
), co tego nie gwarantujeclose
można nazwać wielokrotnie:
Zauważ, że w przeciwieństwie do metody close java.io.Closeable, ta metoda zamykania nie musi być idempotentna. Innymi słowy, wywołanie tej metody zamykania więcej niż raz może mieć widoczny efekt uboczny, w przeciwieństwie do Closeable.close, które nie wymaga żadnego efektu, jeśli zostanie wywołane więcej niż raz. Jednak zdecydowanie zaleca się implementatorom tego interfejsu, aby ich metody zamknięcia były idempotentne.
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
}
}
Ta wersja powinna być teoretycznie poprawna, ponieważ tylkofw
reprezentuje prawdziwy zasób, który należy oczyścić. Thebw
sam nie posiada żadnych zasobów, tylko deleguje dofw
, więc powinno wystarczyć tylko zamknięcie instrumentu bazowegofw
.
Z drugiej strony, składnia jest nieco nieregularna, a także Eclipse wydaje ostrzeżenie, które moim zdaniem jest fałszywym alarmem, ale nadal jest ostrzeżeniem, że trzeba sobie radzić z:
Wyciek zasobów: „bw” nigdy nie jest zamknięte
Więc jakie podejście wybrać? A może brakowało mi jakiegoś innego idiomuprawidłowe jeden?