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 zbwkonstruktor, jegoclose nie zostanie wywołany, a więc instrument bazowyFileWriter 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
    }
}

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?

questionAnswers(8)

yourAnswerToTheQuestion