1.如何优雅的处理Lambda中异常?
java8新增的特性Lambda表达式为我们使用匿名内部类提供了非常简洁的方式。然而,平时在使用lambda的过程发,却发现lambda表达式中如果存在异常,使用try-catch去处理异常,那么这个lambda表达式就会看起来非常臃肿。
在lambda中使用try-catch处理异常:
List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
list.forEach(e -> {
try{
Integer integer = Integer.valueOf(e);
}catch (Exception ex){
System.err.println("发生异常 :"+ex.toString());
}
});
这么做其实也没有什么问题。只不过在视觉上看着非常臃肿,破坏了lambda简洁的效果。除了使用try-catch的方式去处理异常,我们还可以选择throw的方式将异常抛出去。
但是实际上lambda对受检异常和非受检异常的上抛处理方式支持是不同的。
List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
// 非受检异常 编译通过
list.forEach(e-> {
throw new RuntimeException();
});
// 受检异常 编译不通过
list.forEach(e -> {
throw new IOException();
});
可以看到,受检异常是不能抛出去的,编译无法通过。我们就只能使用try-catch或者将异常抛出去从而处理这个异常。
如果我们将异常从方法上再次抛出去呢?
@Test
public void test02() throws IOException{
List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
//编译不通过
list.forEach(e -> {
throw new IOException();
});
}
很难过的是,对于受检异常,这样的做法也是不被允许的。
那么该如何简洁又优雅的处理lambda中的异常呢?
对于非受检异常,使用包装方法来进行处理:
List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
list.forEach(
HandlingConsumerWrapper.handlingConsumerWrapper(e->{
System.out.println(Integer.valueOf(e));
}));
/**
* @ClassName handlingConsumerWrapper
* @Description 对lambda异常进行处理的包装类
* @Date 2019-10-25 下午 6:04
* @Version V1.0
*/
public class HandlingConsumerWrapper {
private static final Logger logger = LoggerFactory.getLogger(HandlingConsumerWrapper.class);
public static <T> Consumer<T> handlingConsumerWrapper(Consumer<T> throwingConsumer){
return i -> {
try{
throwingConsumer.accept(i);
}
catch (Exception e){
logger.error(" 处理lambda中的异常发生异常 "+e.toString());
}
};
}
}
简单来说,就是将本来要传入的Consumer接口的实现,传入handlingConsumerWrapper方法中,并对其中的异常进行处理,然后再返回一个Consumer接口的实现。
这样来处理,在lambda中既可以处理异常又可以保证代码的简洁。不会让lambda显得很臃肿。
对于受检异常,需要自己定义一个函数接口:
/**
* @ClassName ThrowingConsumer
* @Description 封装consumer接口异常的接口
* @Author
* @Date 2019-10-25 下午 6:02
* @Version V1.0
*/
@FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {
//对异常选择上抛 原consumer接口没有上抛 才使得不好处理
void accept (T t)throws Exception;
}
List<String> list = Arrays.asList("海", "上", "月", "是", "天", "上", "月");
list.forEach(
HandlingConsumerWrapper.handlingConsumerWrapper(e->{
File file = new File("");
file.createNewFile();
}));
}
public static <T,E extends Exception> Consumer<T> handlingConsumerWrapper(ThrowingConsumer<T, E> throwingConsumer){
return i -> {
try{
//处理函数接口抛上来的异常
throwingConsumer.accept(i);
}catch (Exception e){
logger.error(" 处理lambda中的异常发生异常 "+e.toString());
}
};
}
这里实际上是定义了一个函数接口ThrowingConsumer,ThrowingConsumer的方法和Consumer接口一致,只不过抛出了异常,这样我们才可以在包装方法中处理受检异常。
上述两种方法实际上都是将异常在包装方法中进行了处理。下面说一下一定要将异常抛出方法的情况:
对于一定想要将异常抛出方法的情况:
//可以将异常抛出来了
@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException, ClassNotFoundException {
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(className -> System.out.println(Class.forName(className))));
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.forEach(rethrowConsumer(System.out::println));
}
//定义可以抛出异常的函数接口
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
//将受查异常作为非受查异常抛出去
throwAsUnchecked(exception);
}
};
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
throw (E) exception;
}
可以看到,其实大体上和try-catch的处理是基本一致的。只不过在将异常catch之后,将异常全部作为非受检异常抛了出去。但是这种情况就可以将异常都上抛给调用的方法进行处理了。
处理lambda的工具类:
package com.uplus.gateway.util.lambda;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* @ClassName LambdaExceptionUtil
* @Description
* @Author
* @Date 2019-10-27 下午 3:16
* @Version V1.0
*/
public class LambdaExceptionUtil {
@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}
@FunctionalInterface
public interface BiConsumer_WithExceptions<T, U, E extends Exception> {
void accept(T t, U u) throws E;
}
@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}
@FunctionalInterface
public interface Supplier_WithExceptions<T, E extends Exception> {
T get() throws E;
}
@FunctionalInterface
public interface Runnable_WithExceptions<E extends Exception> {
void run() throws E;
}
/**
* .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name)))); or .forEach(rethrowConsumer(ClassNameUtil::println));
*/
public static <T, E extends Exception> Consumer<T> rethrowConsumer(Consumer_WithExceptions<T, E> consumer) throws E {
return t -> {
try {
consumer.accept(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
public static <T, U, E extends Exception> BiConsumer<T, U> rethrowBiConsumer(BiConsumer_WithExceptions<T, U, E> biConsumer) throws E {
return (t, u) -> {
try {
biConsumer.accept(t, u);
} catch (Exception exception) {
throwAsUnchecked(exception);
}
};
}
/**
* .map(rethrowFunction(name -> Class.forName(name))) or .map(rethrowFunction(Class::forName))
*/
public static <T, R, E extends Exception> Function<T, R> rethrowFunction(Function_WithExceptions<T, R, E> function) throws E {
return t -> {
try {
return function.apply(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
};
}
/**
* rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
*/
public static <T, E extends Exception> Supplier<T> rethrowSupplier(Supplier_WithExceptions<T, E> function) throws E {
return () -> {
try {
return function.get();
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
};
}
/**
* uncheck(() -> Class.forName("xxx"));
*/
public static void uncheck(Runnable_WithExceptions t) {
try {
t.run();
} catch (Exception exception) {
throwAsUnchecked(exception);
}
}
/**
* uncheck(() -> Class.forName("xxx"));
*/
public static <R, E extends Exception> R uncheck(Supplier_WithExceptions<R, E> supplier) {
try {
return supplier.get();
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
}
/**
* uncheck(Class::forName, "xxx");
*/
public static <T, R, E extends Exception> R uncheck(Function_WithExceptions<T, R, E> function, T t) {
try {
return function.apply(t);
} catch (Exception exception) {
throwAsUnchecked(exception);
return null;
}
}
@SuppressWarnings("unchecked")
private static <E extends Throwable> void throwAsUnchecked(Exception exception) throws E {
throw (E) exception;
}
}