Core Java

Exceptions in Lambdas: An Elegant Solution to a Bit of a Mess

Consider the following function for writing to a file:

The idea behind the method is to allow the user to pass in different implementations of InputStream to the method so that writeToFile can be called for example with a GZIPOuputStream, SnappyOuputStream
(fast compression) or simply a plain FileInputStream.

private static void writeToFile(File file, String value,
        Function<OutputStream, OutputStream> writing) throws IOException{
    try (PrintWriter pw = new PrintWriter(new BufferedOutputStream
            (writing.apply(new FileOutputStream(file))))) {
        pw.write(value);
    }
}

It’s a neat function which can be called like this:

public static void main(String[] args) {
    try {
        //Write with compression
        //DOES NOT COMPILE!!
        writeToFile(new File("test"), "Hello World", GZIPOutputStream::new);
        //Just use the FileOutputStream
        writeToFile(new File("test"), "Hello World", i->i);
    }catch(IOException e){
        //deal with exception as you choose
    }
}

Unfortunately as pointed out in the comment this does not compile!  The reason it doesn’t compile is because the GZIPOutputStream throws an IOException in its constructor. What would have been nice was if the IOException was thrown out of the lambda and could then be dealt with in the try catch block – but that’s not how lambdas work :-(

This is in fact how you have to deal with the exception to get the code to compile:

public static void main(String[] args) {
    try {
        //Write with compression
        //COMPILES BUT SO UGLY
        writeToFile(new File("test"), "Hello World", i -> {
            try {
                return new GZIPOutputStream(i);
            } catch (IOException e) {
                //HOW ARE WE SUPPOSED TO DEAL WITH THIS EXCEPTION??
                throw new AssertionError(e);
            }
        });
        //Just use the FileOutputStream
        writeToFile(new File("test"), "Hello World", i->i);
    }catch(IOException e){
        //deal with exception as you choose
    }
}

Not only is this ugly but you are left with the rather awkward problem of what to do with the IOException. In this case we have just re-packaged inside an AssertionError. See my previous post ‘Cheating with Exceptions’ on the correct way to handle this scenario.

But there is a solution to this problem. Rather than using a java.util.function.Function that takes a value and returns a value, we can create a custom function that takes a value returns a value and throws an Exception. In this way the client code of writeToFile is nice and clean and can deal with the exception in a natural way. Moreover, lambdas are now used in the way they were intended to make our code prettier and easier to understand.

See full code listing below:

package util;
 
import java.io.*;
import java.util.zip.GZIPOutputStream;
 
public class LambdaExceptions {
    public static void main(String[] args) {
        try {
            //Write with compression
            writeToFile(new File("test"), "Hello World", GZIPOutputStream::new);
            //Just use the FileOutputStream
            writeToFile(new File("test"), "Hello World", i->i);
        }catch(IOException e){
            //deal with exception as you choose
        }
    }
    
    private static void writeToFile(File file, String value, 
                       ThrowingFunction<OutputStream, OutputStream, IOException> writing) throws IOException{
        try (PrintWriter pw = new PrintWriter(new BufferedOutputStream
                (writing.apply(new FileOutputStream(file))))) {
            pw.write(value);
        } 
    }
 
    @FunctionalInterface
    public interface ThrowingFunction<I, O, T extends Throwable> {
        O apply(I i) throws T;
    }
}

Daniel Shaya

Daniel has been programming in Java since it was in beta. Working predominantly in the finance industry he has created real time trading and margin risk applications. He is currently a director at OpenHFT where we are building next generation Java low latency products.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button