Java 8
introduces a lot of new API to ease the Functional Programming
paradigm for smaller, concise, smaller and reduce boilerplate code. Functional Programming
doesn't handle the Exceptions
very well.
Java 7
or prior, Exceptions
were handled using try-catch
block, same holds true for Java 8
as well. since lambda expressions are closely bound to its functional interface, hence it cannot throw an exception unless the corresponding functional interface declares the same.
Unchecked Exception
An Unchecked Exception
cannot catch by the compiler
and occurs at run time, hence is also called Runtime Exception
. Since the compiler
cannot capture the Runtime Exception
, it will not give any compilation error but throw an Exception
at Runtime
.
Runtime Exceptions should not be handled or declared
Runtime Exceptions
usually happen due to programming errors
, poor coding
, unseen bug
or wrong logic
, and such Exceptions
should not be caught but let the Exception
occur and deal with changes in logic or programming.
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
class Apple {
public static void main(String[] args) {
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
list.stream().forEach(apple -> {
System.out.println(apple);
throw new RuntimeException("Unchecked Exception in Lambda");
});
}
}
so far the only way to handle the Exception
is to add a try-catch
block
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
class Apple {
public static void main(String[] args) {
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
list.stream().forEach(apple -> {
try {
System.out.println(apple);
throw new RuntimeException("Unchecked Exception in Lambda");
} catch (RuntimeException exception) {
System.out.println(exception.getMessage());
}
});
}
}
Checked Exception
A Checked Exception
is caught by the compiler at Compile Time
only, hence called Compile time Exception
. Checked Exception
can be handled either by re-throwing
the same Exception
or with a try-catch
block.
Checked Exception in Lambda Expression can be handled in 3 ways.
-
Add a try-catch block inside the lambda
-
Extract Exception block to a wrapper method
-
Create a Custom Functional Interface which throws an Exception
-
Create a Custom Functional Interface which extends Functional Interface and overrides existing method
try-catch block in lambda statement
Lambda Expression
allows complete body as well, so the try-catch
block can be incorporated inside the Lambda Expression
. It's the easiest way to handle exceptions but will lose the conciseness and readability of the code.
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
class Apple {
public static void main(String[] args) {
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
list.stream().forEach(apple -> {
try {
System.out.println(apple);
throw new Exception("Checked Exception in Lambda");
} catch (Exception e) {
System.out.println(exception.getMessage());
}
});
}
}
Extract Exception block to a wrapper method
Lambda Expression
is known for its conciseness, shorter code, less boilerplate code, and readability. adding a try-catch
block inside lambda will lose all the above characteristics, a simpler solution would be to extract the exception block to another method and call the method from Lambda Expression
.
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
class Apple {
public static void main(String[] args) {
Consumer<String> consumer = (apple) -> {
try {
System.out.println(apple);
throw new RuntimeException("Checked Exception in Lambda");
} catch (RuntimeException exception) {
System.out.println(exception.getMessage());
}
};
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
/* Exception block to a wrapper method using consumer */
list.stream().forEach(consumer);
/* Exception block to a wrapper method using another method */
list.stream().forEach(Apple::consumer);
}
public static void consumer(String apple) {
try {
System.out.println(apple);
throw new RuntimeException("Checked Exception in Lambda");
} catch (RuntimeException exception) {
System.out.println(exception.getMessage());
}
}
}
Create a Custom Functional Interface which throws Exception
Extracting the Exception
block to another wrapper method will make the lambda code concise but will add the overhead of creating method objects and executing the try-catch
block for each call.
the best solution is to create a custom functional interface
whose abstract
method throws the Exception
and another static method
which accepts the customer functional interface
, will catch the Exception
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import static org.wesome.java8.ThrowingConsumer.wrapper;
@FunctionalInterface
interface ThrowingConsumer<T, E extends Exception> {
void accept(T t) throws E;
static <T> Consumer<T> wrapper(ThrowingConsumer<T, Exception> t) {
return arg -> {
try {
t.accept(arg);
} catch (Exception e) {
System.out.println(exception.getMessage());
}
};
}
}
class Apple {
public static void main(String[] args) {
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
list.forEach(wrapper(System.out::println));
}
}
Create a Custom Functional Interface which extends the Functional Interface and overrides the existing method
some Lambda functions
have predefined input type and any Custom Functional Interface
will not be accepted, in these cases, create a Custom Functional Interface
, extends the Functional Interface
which is required, and override
the default method
.
package org.wesome.java8;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
@FunctionalInterface
interface CustomFunctionalInterface<T> extends Consumer<T> {
@Override
default void accept(T elem) {
try {
acceptThrows(elem);
} catch (Exception exception) {
System.out.println(exception.getMessage());
}
}
void acceptThrows(T elem) throws Exception;
}
class Apple {
public static void main(String[] args) {
CustomFunctionalInterface<String> consumer = (apple) -> {
System.out.println(apple);
throw new Exception("Checked Exception in Lambda");
};
List<String> list = Arrays.asList("Fuji", "Gala", "Macintosh");
list.forEach(consumer);
}
}