Składnia try-with-resources Java 7 (znana również jako blok ARM ( Automatyczne zarządzanie zasobami )) jest przyjemna, krótka i prosta, gdy używasz tylko jednego AutoCloseable
zasobu. Nie jestem jednak pewien, jaki jest poprawny idiom, gdy muszę zadeklarować wiele zasobów, które są od siebie zależne, na przykład a FileWriter
i a, BufferedWriter
które to zawijają. Oczywiście to pytanie dotyczy każdego przypadku, gdy niektóre AutoCloseable
zasoby są opakowane, a nie tylko te dwie konkretne klasy.
Wymyśliłem trzy następujące alternatywy:
1)
Naiwny idiom, który widziałem, polega na zadeklarowaniu 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ż element bazowy FileWriter
nie jest zadeklarowany w zmiennej, nigdy nie zostanie zamknięty bezpośrednio w wygenerowanym finally
bloku. Zostanie zamknięty tylko close
metodą zawijania BufferedWriter
. Problem polega na tym, że jeśli wyjątek zostanie wyrzucony z bw
konstruktora, close
to nie zostanie on wywołany i dlatego element bazowy FileWriter
nie zostanie zamknięty .
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
}
}
Tutaj zarówno zasób bazowy, jak i zasób opakowujący są zadeklarowane w zmiennych zarządzanych przez ARM, więc oba z pewnością zostaną zamknięte, ale element bazowy fw.close()
zostanie wywołany dwukrotnie : nie tylko bezpośrednio, ale także poprzez opakowanie bw.close()
.
Nie powinno to stanowić problemu dla tych dwóch konkretnych klas, które obie implementują Closeable
(co jest podtypem AutoCloseable
), których umowa stanowi, że close
dozwolone jest wielokrotne wywoływanie :
Zamyka ten strumień i zwalnia wszystkie powiązane z nim zasoby systemowe. Jeśli strumień jest już zamknięty, wywołanie tej metody nie ma żadnego efektu.
Jednak w ogólnym przypadku mogę mieć zasoby, które implementują tylko AutoCloseable
(i nie Closeable
), co nie gwarantuje, że close
można to wywołać wiele razy:
Zauważ, że w przeciwieństwie do metody close java.io.Closeable, ta metoda close nie musi być idempotentna. Innymi słowy, wywołanie tej metody close więcej niż raz może mieć widoczny efekt uboczny, w przeciwieństwie do Closeable.close, które jest wymagane, aby nie wywoływać żadnego efektu, jeśli zostanie wywołane więcej niż raz. Jednak implementujących ten interfejs zdecydowanie zachęca się, aby ich bliskie metody 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ż tylko fw
reprezentuje prawdziwy zasób, który należy wyczyścić. Samo bw
nie zawiera żadnego zasobu, a jedynie deleguje go do fw
, więc powinno wystarczyć tylko zamknięcie bazowego fw
.
Z drugiej strony składnia jest nieco nieregularna, a także Eclipse generuje ostrzeżenie, które uważam za fałszywy alarm, ale nadal jest to ostrzeżenie, z którym trzeba sobie poradzić:
Wyciek zasobów: „bw” nigdy nie jest zamykany
Więc jakie podejście wybrać? A może przegapiłem jakiś inny idiom, który jest właściwy ?
public BufferedWriter(Writer out, int sz)
może rzucić IllegalArgumentException
. Mogę również rozszerzyć BufferedWriter o klasę, która wyrzuci coś ze swojego konstruktora lub utworzy dowolne niestandardowe opakowanie, którego potrzebuję.
BufferedWriter
Konstruktor łatwo wyrzucić wyjątek. OutOfMemoryError
jest prawdopodobnie najczęstszym, ponieważ przydziela sporą część pamięci dla bufora (chociaż może wskazywać, że chcesz ponownie uruchomić cały proces). / Trzeba flush
TWOJEJ BufferedWriter
jeśli nie blisko i chcesz zachować zawartość (generalnie tylko i uzasadnienie bez wyjątku). FileWriter
przejmuje wszystko, co jest „domyślnym” kodowaniem pliku - lepiej jest to wyraźnie określić.