2014-12-09 16 views
11

Si consideri il seguente codice:Le operazioni del terminale sui flussi chiudono la sorgente?

Path directory = Paths.get(/* some directory */); 
Files.list(directory).forEach(System.out::println); 

Fa un funzionamento del terminale (come forEach) chiudere il file sottostante che è stato aperto?

far riferimento alle parti pertinenti del javadoc di Files.list:

Il flusso restituito incapsula un DirectoryStream. Se è richiesto lo smaltimento tempestivo delle risorse del file system, è necessario utilizzare il costrutto try-with-resources per garantire che il metodo di chiusura del flusso venga richiamato al termine delle operazioni dello stream.

Se non chiama Stream.close(), quale sarebbe l'alternativa migliore per chiamarlo mentre produce codice gestibile?

+1

Questa domanda è simile a http://stackoverflow.com/questions/26997240/do-terminal-operations-close-the-stream?rq=1 con l'eccezione di chiedere anche quali "contromisure" prendere. – TheConstructor

+2

Anche simile a [questa domanda] (http://stackoverflow.com/questions/22921623/closing-streams-in-the-middle-of-pipelines), che ha una lunga risposta che descrive la motivazione di questo comportamento. – Lii

risposta

16

Gli operatori di terminale NON chiudono il flusso automaticamente. Considerare questo codice:

Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed")); 
list.forEach(System.out::println); 

Questo NON stampa "Chiuso".

Tuttavia, il seguente fa di stampa "Chiuso":

try (Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed"))) { 
    list.forEach(System.out::println); 
} 

Quindi il modo migliore per farlo è quello di utilizzare il meccanismo try-con-risorse.

1

Quindi un rapido controllo rivela che forEach non chiude la DirectoryStream:

import java.io.IOException; 
import java.nio.file.Files; 
import java.nio.file.Path; 
import java.nio.file.Paths; 
import java.util.function.Consumer; 
import java.util.stream.Stream; 

/** 
* Created for http://stackoverflow.com/q/27381329/1266906 
*/ 
public class FileList { 
    public static void main(String[] args) { 
     Path directory = Paths.get("C:\\"); 
     try { 
      Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Close called")); 
      list.forEach(System.out::println); 
      // Next Line throws "java.lang.IllegalStateException: stream has already been operated upon or closed" even though "Close called" was not printed 
      list.forEach(System.out::println); 
     } catch (IOException | IllegalStateException e) { 
      e.printStackTrace(); // TODO: implement catch 
     } 

     // The mentioned try-with-resources construct 
     try (Stream<Path> list = Files.list(directory)) { 
      list.forEach(System.out::println); 
     } catch (IOException | IllegalStateException e) { 
      e.printStackTrace(); // TODO: implement catch 
     } 

     // Own helper-method 
     try { 
      forEachThenClose(Files.list(directory), System.out::println); 
     } catch (IOException | IllegalStateException e) { 
      e.printStackTrace(); // TODO: implement catch 
     } 
    } 

    public static <T> void forEachThenClose(Stream<T> list, Consumer<T> action) { 
     try { 
      list.forEach(action); 
     } finally { 
      list.close(); 
     } 
    } 
} 

vedo le due mitigazioni presentati:

  1. uso try-with-risorse, come indicato nel Files.list JavaDoc
  2. scrivere il proprio metodo di supporto che utilizza un blocco finale

Ciò che è più gestibile dipende probabilmente da quanti metodi di supporto sono necessari.

Problemi correlati