2015-04-24 13 views
8

Sto giocando con modi per scrivere un set di oggetti in un file. Perché l'implementazione sottostante usando Iterable.forEach() non viene compilata? In Eclipse, ricevo il messaggio che una IOException non viene gestita. Ciò è particolarmente confuso dal momento che sembra che gestisca IOExceptions.Come gestire IOException in Iterable.forEach?

Ovviamente, il seguito funziona. Sono interessato al motivo per cui quanto sopra non funziona e come risolverlo.

for (Object o : objects) { bw.write(o.toString()); } 

Ho controllato la documentazione Consumer e Iterable, e nessuno di loro sembrano suggerire come risolvere questo.

+0

Puoi compilarlo tramite la riga di comando? Quale versione di Eclipse? – javajavajava

risposta

9

Precursore: The Catch or Specify Requirement.

Scriviamo lambda come una classe anonima:

objects.forEach(new Consumer<Object>() { 
    public void accept(Object o) { 
     bw.write(o.toString()); 
    } 
}); 

siamo maneggiarlo? Dovrebbe essere chiaro che non lo siamo.

Quando scriviamo un'espressione lambda, stiamo dichiarando il corpo di un metodo. Inoltre, non possiamo dichiarare una clausola throws per un lambda.

L'unico "modo intorno ad esso" sarebbe quello di fare qualcosa di simile al seguente:

try (BufferedWriter bw = new BufferedWriter(
    new OutputStreamWriter(
     new FileOutputStream("out.txt"), "UTF-8"));) { 

    objects.forEach((o) -> { 
     try { 
      bw.write(o.toString())); 
     } catch(IOException kludgy) { 
      throw new RuntimeException(kludgy); 
     } 
    }); 

} catch (RuntimeException kludgy) { 
    Throwable cause = kludgy.getCause(); 
    if (cause instanceof IOException) { 
     IOException e = (IOException) cause; 
     // handle exception 
    } else { 
     throw kludgy; 
    } 
} 

Ma questo non è veramente buono.

Invece, non utilizzare forEach in un contesto che genera un'eccezione controllata o catturare l'eccezione nel corpo del lambda.

+0

Questo è quello che stavo cercando. Grazie! –

2

Il problema è che il metodo accept nell'interfaccia Consumer non è dichiarato per generare un'eccezione. Pertanto non è possibile utilizzare un metodo che genera un'eccezione controllata nel lambda.

La soluzione è utilizzare invece un ciclo per ciascun ciclo.

+1

Ahimè, questa è una delle carenze dell'attuale implementazione lambda che mi ha colpito la testa più di una volta! :) –

6

Se tutto ciò che si preoccupa è la stampa di stringhe su un file, utilizzare uno PrintStream o forse PrintWriter anziché le altre classi Writer. La caratteristica notevole di PrintStream e PrintWriter è che le loro operazioni di stampa non generano IOException. Chiedono inoltre toString su oggetti automaticamente, il che rende le cose molto convenienti:

public void write1(Iterable<?> objects) { 
    try (PrintStream ps = new PrintStream("printout.txt", "UTF-8")) { 
     objects.forEach(ps::println); 
    } catch (IOException ioe) { 
     // handle 
    } 
} 

Se siete preoccupati per gli errori, è possibile chiamare PrintStream.checkError, anche se questo non vi dice eventuali specifiche su qualsiasi errore che potrebbe essersi verificato .

La domanda generale rimane, tuttavia, su cosa fare se si desidera chiamare un metodo di lancio di eccezioni da un contesto (come forEach) che non lo consente. Questo è solo fastidioso da affrontare, anche se solo moderatamente. Tuttavia richiede alcune impostazioni. Supponiamo di voler scrivere uno Consumer che lanci uno IOException.Dobbiamo dichiarare la nostra propria interfaccia funzionale:

interface IOConsumer<T> { 
    void accept(T t) throws IOException; 
} 

Ora abbiamo bisogno di scrivere una funzione che converte un IOConsumer ad un Consumer. Lo fa convertendo qualsiasi IOException in uno UncheckedIOException, un'eccezione creata per questo scopo.

static <T> Consumer<T> wrap(IOConsumer<? super T> ioc) { 
    return t -> { 
     try { 
      ioc.accept(t); 
     } catch (IOException ioe) { 
      throw new UncheckedIOException(ioe); 
     } 
    }; 
} 

Con questi a posto, possiamo ora riscrivere l'esempio originale come segue:

public void write2(Iterable<?> objects) { 
    try (BufferedWriter bw = new BufferedWriter(
      new OutputStreamWriter(
       new FileOutputStream("out.txt"), "UTF-8"))) { 
     objects.forEach(wrap(o -> bw.write(o.toString()))); 
    } catch (IOException|UncheckedIOException e) { 
     //handle exception 
    } 
} 
1

generalmente noi non scrivere dati su file se alcuna eccezione occures. tenendo presente questo, possiamo scrivere il nostro own consumer which will wrap the checked exception into an unchecked exception. in questo modo potresti farla franca con l'errore del tempo di compilazione. prova qui sotto codice

import java.io.*; 
import java.util.*; 
public class HelloWorld{ 

    public static void main(String []args){ 
     write(Arrays.asList(1,2,3)); 
    } 

    public static void write(Iterable<?> objects) { 
    try (BufferedWriter bw = new BufferedWriter(
     new OutputStreamWriter(
      new FileOutputStream("c:\\out.txt"), "UTF-8"))) { 

     objects.forEach(o->myConsumerThrowsRuntimeException(bw,o.toString())); //Unhandled exception type IOException 

    } catch (Exception e) { 
    //handle exception 
    } 
    } 
    public static void myConsumerThrowsRuntimeException(Writer writer , String data){ 
     try{ 
      writer.write(data); 
     }catch(IOException e){ 

      throw new RuntimeException("Cannot write Data error is "+e.getMessage()); 
     } 
    } 
} 
Problemi correlati