2016-06-07 8 views
20

C'è un buon modo di usare try-with-resources quando si apre un InputStream in un costruttore e quindi lo si passa a un super costruttore?Try-with-resources quando si chiama super-costruttore

Fondamentalmente quello che voglio fare è questo:

public class A { 
    public A(InputStream stream) { 
     // Do something with the stream but don't close it since we didn't open it 
    } 
} 

public class B { 
    public B(File file) { 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      super(new FileInputStream(file)); 
     } 
    } 
} 

Ma, naturalmente, dal momento che super deve essere la prima istruzione nel costruttore questo non è permesso. C'è un buon modo per ottenere questo?

+2

Avrei il chiamante fornire il flusso di input a 'pubblico B (InputStream in)' e chiuderlo anche. Nessun motivo per rendere la classe derivata meno versatile della classe base. – EJP

+0

Più versatile, ma anche più ingombrante da usare. Potrei supportare entrambi, ma non avendo il costruttore 'B (File file)' non è un'opzione. – Raniz

+2

Mi sembra che il tuo problema derivi dal consumo del flusso all'interno del costruttore di A. Se non fosse il caso, dovresti semplicemente archiviare il flusso in una variabile di istanza e rendere A 'AutoClosable'. –

risposta

25

Considerare l'utilizzo di un metodo factory statico anziché utilizzare direttamente il costruttore. Fare almeno B 's costruttore privato, e creare un metodo come

private B(InputStream is) { 
    super(is); 
    // Whatever else is needed 
} 

public static B newInstance(File file) { 
    B result; 
    try (FileInputStream stream = new FileInputStream(file)) { 
     result = new B(stream); 
    } 
    // Further processing 
    return result; 
} 
+0

Sì, sembra che questa sia la strada da percorrere – Raniz

2

Un'altra strada da percorrere:

public class A { 
    protected A(){ 
     // so he can't be called from the outside, subclass ensure that init is done properly. 
    } 

    public A(InputStream stream) { 
     init(stream); 
    } 
    // not be able to call it from outside 
    protected final init(InputStream is){ 
     //here goes the code 
    } 
} 

public class B { 
    public B(File file) { 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      init(stream); 
     } 
    } 
} 

sto postando questo qui come una possibile risposta, tuttavia io sono qui consdering:

  1. È possibile aggiornare il codice di un
  2. Si sta spostando il codice del costruttore per un metodo init, grazie alle protetta costruttore vuoto arg, solo le sottoclassi devono gestire correttamente la chiamata per iniziare. Alcuni potrebbero vederlo non così ben progettato. Il mio punto di vista è che presto la tua sottoclasse qualcosa, devi sapere di più su questo solo quando lo usi.
+0

Come hai detto, richiede l'accesso al codice sorgente di * A * e non funzionerà se ci sono variabili finali in * A * che devono essere inizializzate dai contenuti in il flusso. – Raniz

+2

si dovrebbe fare 'init'' final', altrimenti una sottoclasse potrebbe sovrascriverla e interrompere il costruttore di 'A'. Penso che si chiami constructor leak o qualcosa del genere. – Oebele

+0

buon punto l'ho aggiunto. – Walfrat

-1

Purtroppo non ho un compilatore a disposizione per testare ma non potresti fare come segue.

public class B { 
    private static InputStream file2stream(File f){ 
     // We open the stream so we need to ensure it's properly closed 
     try (FileInputStream stream = new FileInputStream(file)) { 
      return stream; 
     }catch(/*what you need to catch*/){ 
      //cleanup 
      // possibly throw runtime exception 
     } 
    } 
    public B(File file) { 
     super(file2stream(file)) 
    } 
} 
+3

Sfortunatamente, la fine del blocco 'try' viene raggiunta prima della fine di' file2stream', chiudendo il file prima che possa essere passato in 'super'. –

+0

Potrei aver sbagliato la domanda, ma ho pensato che fosse questo il punto. Capisco la domanda in modo tale che l'unica cosa che è stata sottoposta ad essere protetta da provare è stata la costruzione di InputStream, se non è così il modo in cui deve essere compreso? – lijat

+1

Non hai capito il commento di Hank D, penso. Il problema è che in try-with-resources, la risorsa (stream) che è stata aperta viene chiusa alla fine del blocco.L'istruzione 'return' non impedisce questo (o non ci sarebbe un punto al' try'). Quindi quando 'file2stream' restituisce la risorsa, è ** già chiusa **. Notare anche la differenza tra * try-with-resources * (che è fatto per chiudere le risorse) e * try-catch * che viene fatto per rilevare le eccezioni e non è quello discusso qui. – RealSkeptic

Problemi correlati