Czy instancje Java 8 Stream powinny zawsze być close () 'd?

12

Quoth the Javadoc :

Strumienie mają metodę BaseStream.close () i implementują funkcję AutoCloseable, ale prawie wszystkie instancje strumienia nie muszą być tak naprawdę zamykane po użyciu. Zasadniczo tylko strumienie, których źródłem jest kanał IO (takie jak te zwracane przez Files.lines (Path, Charset)) będą wymagać zamknięcia. Większość strumieni jest wspierana przez kolekcje, tablice lub funkcje generujące, które nie wymagają specjalnego zarządzania zasobami. (Jeśli strumień wymaga zamknięcia, można go zadeklarować jako zasób w instrukcji try-with-resources).

„Prawie wszystkie” i „ogólnie” są niejasne - jeśli piszesz bibliotekę i oddzielasz źródło swojego strumienia od użytkowników tego strumienia, zawsze musisz zadać sobie pytanie - „powinienem zamknąć to?" Strumienie wspierane przez IO muszą zostać zamknięte, ponieważ operacje terminali nie wywołują close, dlatego zawsze muszę pamiętać / dokumentować, skąd pochodzi mój strumień, lub zawsze muszę to robić close.

Chyba nuklearną opcją byłoby nie zwracanie strumieni z metod lub akceptowanie parametrów strumienia, co jest sentymentem, który został powtórzony przez niektóre osoby z zespołu JDK. Uważam, że jest to zbyt ograniczające, biorąc pod uwagę praktyczną przydatność strumieni.

Jakie są najlepsze praktyki dotyczące zamykania strumieni? Szukałem w Internecie odpowiedzi na to pytanie od niektórych osób z JDK, które zwykle są aktywne w podobnych pytaniach społeczności, ale nie znalazłem nic istotnego.

RuslanD
źródło
Nie jestem programistą Java, ale użyłbym tych reguł: - Jeśli strumień zostanie przekazany jako argument, udokumentuj, że osoba dzwoniąca musi zamknąć strumień, jeśli to konieczne; - Jeśli strumień jest zwracany z funkcji, załóż, że musisz go zamknąć.
Bart van Ingen Schenau,

Odpowiedzi:

6

Jak już powiedziałeś, w Javie musisz dokładnie wiedzieć, kto jest odpowiedzialny za zwolnienie którego zasobu, abyś mógł wprowadzić odpowiednie konstrukcje try-catch, try-with-resources lub jakoś delegować to zadanie.

Jedyną rzeczą, którą możesz wyczyścić za pomocą GC, jest 100% czysta pamięć.
Jeśli nie może być jakieś inne zasoby mieszane w, jedyną rzeczą, jaką możesz zrobić, to po prostu rozsądnie grać to bezpieczne.

Deduplikator
źródło
Więc w zasadzie closejest jedyną bezpieczną alternatywą? Myślę, że pytanie brzmi: jak sprawić, by kod nie wyglądał zbyt brzydko za każdym razem, gdy Stream byłby przydatny w użyciu.
RuslanD,
1
Tak, jeśli mogą istnieć zasoby inne niż pamięć, jedyną bezpieczną rzeczą do zrobienia jest założenie, że . Nie można tego obejść, nawet jeśli Java jest bardzo nieodpowiednia dla zasobów spoza GC.
Deduplicator,
Więc jeśli jest to tylko strumień między dwiema kolekcjami (czysta pamięć), zamknięcie nie jest konieczne?
Amalgovinus
@Amalgovinus poprawnie
Brad Cupit
5

Jeśli chodzi o „najlepsze praktyki”, myślę, że dobrym pomysłem jest stosowanie konwencji nazewnictwa w przypadku metod zwracających „strumienie zasobów”.

Jeśli trzeba close()edytować strumień, należy wywołać metodę fabryczną open()lub openStream(). Wywołaj metody, które konstruują efemeryczne strumienie stream(), zgodnie z konwencją ustanowioną przez SDK. Zawsze używaj metody javadoc, aby ostrzec klienta, że ​​musi close()to zrobić.

public interface StreamingServer<RECORD> {
    /** 
     * Return a memory-efficient record stream from {@code source}.
     * Clients <em>must</em> call {@link Stream#close} to dispose the
     * stream.
     */
    Stream<RECORD> openStream(URI source) throws IOException;
}

Chciałbym, aby autorzy SDK nie zdecydowali się na umieszczenie AutoCloseablew klasie strumienia podstawowego. Wyraźny ResourceStreampodtyp, który w trywialny sposób wdraża AutoCloseable, uczyniłby różne umowy oczywistymi. Wówczas nie można było zamknąć Streamniepotrzebnego i można było wykryć potencjalnie niewłaściwe zarządzanie ResourceStreamnarzędziami do analizy statycznej.

W zależności od potrzeb twojej bazy kodu (i twojej zdolności do egzekwowania konwencji podczas przeglądania kodu) możesz sam stworzyć ściśle wymaganą podklasę strumienia. Lub jeśli chcesz zbudować własne narzędzia analizy statycznej, adnotację metody, która bezpośrednio oznacza zarządzane zasoby.

@RequiresClose
Stream<RECORD> openStream(URI source) throws IOException { ... }
Jason Trump
źródło