javaexceptiontry-catchioexceptionbufferedwriter

How to use try-catch (IOException) for BufferedWriter.write nested in if-else?


I have a block to write data(string) using BufferedWriter, but compiler keeps saying error: unreported exception IOException; must be caught or declared to be thrown this.dataContainer.stream().forEach( line -> writer.write(line) );

The BufferWriter.write() is already inside a try-catch block. Is the error caused by it nested inside if-else? How shall it be written?

void saveData() throws IOException {
        
String filename;
Path filePath; 
BufferedWriter writer;
    
filename = "missionImpossible9.csv";
filePath = this.dirPath.resolve(filename); //make path of this file
writer = Files.newBufferedWriter(filePath); 
        
try {
    if (condition1) {
       this.dataContainer1.stream().forEach( line -> writer.write(line) );
     } else {
       this.dataContainer2.stream().forEach( line -> writer.write(line) );
     }
     writer.close();
} catch (IOException err){
    err.getStackTrace();}
}

Solution

  • The problem is that the method write() of BufferedWriter is declared as throwing IOException, but you're calling it inside a Consumer<?> (the .forEach()) which is not supposed to throw any checked exception:

    this.dataContainer1.stream().forEach( line -> writer.write(line) );
    

    The expression line -> writer.write(line) is a lambda expression implementing the functional interface Consumer, and you can't throw checked exceptions (such as IOException) inside a lambda.

    The way to solve the issue is to catch the IOException inside the lambda itself, and rethrow it wrapped into a RuntimeException (which is unchecked):

    this.dataContainer1.stream().forEach(line -> {
            try {
                writer.write(line); //try code that throws a checked exception
            } catch (IOException e) { //catch that checked exception
                throw new RuntimeException(e); //rethrow it as the cause of a runtime (unchecked) exception
            }
        });
    

    At this point you don't need to catch IOException any longer (in any case, if an IOException occurs from write() you will receive it as a RuntimeException having cause that IOException.

    Note: there are nicer ways to keep the type IOException without the compiler complaining, which is called Sneaky Throw. You may want to read this article about it: Sneaky throws article. Generally speaking it's not suggested to silent a checked exception like this, but sometimes there's no other choice (for example when you want to run code inside a lambda expression and you need to use a functional interface that doesn't throw, such as Consumer).