Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 - throw multiple generic checked exceptions in lambda

In a project I am working at, I have found a class which wraps all methods of its super-class in some elaborate exception handling. It looks similar to that:

public void method1() throws ExceptionA {
    String exceptionString = "";
    try {
        super.method1();
    } catch (ExceptionA e) {
         exceptionString = // <convert the exception to string in an elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

public void method2() throws ExceptionB, ExceptionC {
    String exceptionString = "";
    try {
        super.method2();
    } catch (ExceptionB | ExceptionC e) {
         exceptionString = // <convert the exception to string in elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

// ... <a bunch of other methods like this>

I immediately though "Wow, how could would it be to have one generic wrapper and just call it in every of these methods. The class would be like 10x shorter!". So I got to work.

This is where I got stuck:

private interface ThrowingMethod<E extends Exception> {
    void run() throws E;
}

public <E extends Exception> void wrapMethod(ThrowingMethod<E> method) throws E {
    String exceptionString = "";
    try {
        method.run();
    } catch (Exception e) {
         exceptionString = // <convert the exception to string in an elaborate way>
         throw e;
    } finally {
         // <an elaborate logger call which uses value of exceptionString>
    }
}

public void method1() throws ExceptionA {
    wrapMethod(super::method1); // works
}

public void method2() throws ExceptionB, ExceptionC {
    wrapMethod(super::method2); // Error in Eclipse: "Unhandled exception type Exception"
}

// ... <a bunch of other methods like this>

In conclusion, this approach works for methods that throws only one type of checked exception. When method throws multiple checked exceptions, Java assumes that the exception type is Exception.

I tried to add more generic parameters to ThrowingMethod and wrapMethod but it doesn't change anything.

How can I get a functional interface to work with multiple generic exceptions?

like image 379
NO_NAME Avatar asked Sep 28 '18 15:09

NO_NAME


People also ask

Can we throw checked exception in lambda expression?

A lambda expression cannot throw any checked exception until its corresponding functional interface declares a throws clause.

How does Lambda handle checked exceptions?

The most straightforward way would be to use a try-catch block, wrap the checked exception into an unchecked exception and rethrow it: List<Integer> integers = Arrays. asList(3, 9, 7, 0, 10, 20); integers. forEach(i -> { try { writeToFile(i); } catch (IOException e) { throw new RuntimeException(e); } });

Can we use try-catch in Lambda?

In Java, we can only handle exceptions through the try-catch block, and this hasn't changed for the lambda expression. Let's say we're going to develop a simple web crawler. The crawler will take a list of URLs in a string as an argument and save the content of the text file in a text file.

Can we use try-catch in Lambda Java?

Add a try/catch block to the lambda expression. Create an extracted method, as in the unchecked example. Write a wrapper method that catches checked exceptions and rethrows them as unchecked.


1 Answers

When you expand your interface to use two type variables, i.e.

private static interface ThrowingMethod<E1 extends Exception,E2 extends Exception> {
    void run() throws E1, E2;
}

public <E1 extends Exception,E2 extends Exception>
void wrapMethod(ThrowingMethod<E1,E2> method) throws E1,E2 {
    // same as before
}

the rules regarding the type inference do not change and they are the same for both type variables. E.g. you can still use

public void method1() throws ExceptionA {
    wrapMethod(super::method1);
}

as before, as the compiler simply infers the same single exception type for both type variables.

For the method declaring two exceptions, it won’t pick up one for the first type variable and the other for the second; there is no rule which could tell the compiler which exception to use for which type variable.

But you can help the compiler out in this case, e.g.

public void method2() throws ExceptionB, ExceptionC {
    wrapMethod((ThrowingMethod<ExceptionB, ExceptionC>)super::method2);
}

which is the best you can get with this approach.

like image 146
Holger Avatar answered Sep 27 '22 16:09

Holger