共计 5059 个字符,预计需要花费 13 分钟才能阅读完成。
简介
最近发现很多小伙伴还不晓得如何在 lambda 表达式中优雅的解决 checked exception,所以明天就重点和大家来探讨一下这个问题。
lambda 表达式自身是为了不便程序员书写不便的工具,应用 lambda 表达式能够让咱们的代码更加简洁。
可能大多数小伙伴在应用的过程中素来没有遇到过外面蕴含异样的状况,所以对这种在 lambda 表达式中异样的解决可能没什么教训。
不过没关系,明天咱们就来一起探讨一下。
lambda 表达式中的 checked exception
java 中异样的类型,大家应该是耳熟能详了,具体而言能够有两类,一种是 checked exception, 一种是 unchecked exception。
所谓 checked exception 就是须要在代码中手动捕捉的异样。unchecked exception 就是不须要手动捕捉的异样,比方运行时异样。
首先咱们定义一个 checked exception,间接继承 Exception 就好了:
public class MyCheckedException extends Exception{
@java.io.Serial
private static final long serialVersionUID = -1574710658998033284L;
public MyCheckedException() {super();
}
public MyCheckedException(String s) {super(s);
}
}
接下来咱们定义一个类,这个类中有两个办法,一个抛出 checked exception,一个抛出 unchecked exception:
public class MyStudents {public int changeAgeWithCheckedException() throws MyCheckedException {throw new MyCheckedException();
}
public int changeAgeWithUnCheckedException(){throw new RuntimeException();
}
}
好了,咱们首先在 lambda 表达式中抛出 CheckedException:
public static void streamWithCheckedException(){Stream.of(new MyStudents()).map(s->s.changeAgeWithCheckedException()).toList();}
这样写在现代化的 IDE 中是编译不过的,它会提醒你须要显示 catch 住 CheckedException,所以咱们须要把下面的代码改成上面这种:
public static void streamWithCheckedException(){Stream.of(new MyStudents()).map(s-> {
try {return s.changeAgeWithCheckedException();
} catch (MyCheckedException e) {e.printStackTrace();
}
}).toList();}
这样做是不是就能够了呢?
再思考一个状况,如果 stream 中不止一个 map 操作,而是多个 map 操作,每个 map 都抛出一个 checkedException, 那岂不是要这样写?
public static void streamWithCheckedException(){Stream.of(new MyStudents()).map(s-> {
try {return s.changeAgeWithCheckedException();
} catch (MyCheckedException e) {e.printStackTrace();
}
}).map(s-> {
try {return s.changeAgeWithCheckedException();
} catch (MyCheckedException e) {e.printStackTrace();
}
}).
toList();}
切实是太难看了, 也不不便书写,那么有没有什么好的办法来解决,lambda 中的 checked 异样呢?方法当然是有的。
lambda 中的 unchecked exception
下面例子中咱们抛出了一个 checked exception,那么就必须在 lambda 表达式中对异样进行捕获。
那么咱们可不可以换个思路来考虑一下?
比方,把下面的 checked exception, 换成 unchecked exception 会怎么样呢?
public static void streamWithUncheckedException(){Stream.of(new MyStudents()).map(MyStudents::changeAgeWithUnCheckedException).toList();}
咱们能够看到程序能够失常编译通过,能够缩小或者简直不须要应用 try 和 catch, 这样看起来,代码是不是简洁很多。
那么咱们是不是能够思考把 checked exception 转换成为 unchecked exception,而后用在 lambda 表达式中,这样就能够简化咱们的代码,给程序员以更好的代码可读性呢?
说干就干。
根本的思路就是把传入的 checked exception 转换为 unchecked exception,那么怎么转换比拟适合呢?
这里咱们能够用到 JDK 中的类型推断,通过应用泛型来达到这样的目标:
public static <T extends Exception,R> R sneakyThrow(Exception t) throws T {throw (T) t;
}
这个办法接管一个 checked exception,在外部强制转换之后,抛出 T。
看看在代码中如何应用:
public static void sneakyThrow(){Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();}
代码能够编译通过,这阐明咱们曾经把 checked 异样转换成为 unchecked 异样了。
运行之后你能够失去上面的输入:
Exception in thread "main" java.io.IOException
at com.flydean.Main.lambda$sneakyThrow$1(Main.java:28)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.Streams$StreamBuilderImpl.forEachRemaining(Streams.java:411)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)
at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)
at java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)
at java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)
at com.flydean.Main.sneakyThrow(Main.java:28)
at com.flydean.Main.main(Main.java:9)
从日志中,咱们能够看出最初抛出的还是 java.io.IOException,然而如果咱们尝试对这个异样进行捕捉:
public static void sneakyThrow(){
try {Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();}catch (IOException e){System.out.println("get exception");
}
}
在编译器中会提醒编译不通过,因为代码并不会抛出 IOException。如果你把 IOException 批改为 RuntimeException, 也没法捕捉到最初的异样。
只能这样批改:
public static void sneakyThrow(){
try {Stream.of(new MyStudents()).map(s -> SneakilyThrowException.sneakyThrow(new IOException())).toList();}catch (Exception e){System.out.println("get exception");
}
}
能力最终捕捉到 stream 中抛出的异样。所以如果你应用了我这里说的这种异样转换技巧,那就必须要特地留神这种异样的捕捉状况。
对 lambda 的最终革新
下面能够封装异样了是不是就实现了咱们的工作了呢?
并不是,因为咱们在 map 中传入的是一个 Function 而不是一个专门的异样类。所以咱们须要对 Function 进行额定的解决。
首先 JDK 中的 Function 中必须实现这样的办法:
R apply(T t);
如果这个办法外面抛出了 checked Exception,那么必须进行捕捉,如果不想捕捉的话,咱们能够在办法申明中抛出异样,所以咱们须要从新定义一个 Function,如下所示:
@FunctionalInterface
public interface FunctionWithThrow<T, R> {R apply(T t) throws Exception;
}
而后再定义一个 unchecked 办法,用来对 FunctionWithThrow 进行封装,通过捕捉抛出的异样,再次调用 sneakyThrow 进行 checked 异样和 unchecked 异样的转换:
static <T, R> Function<T, R> unchecked(FunctionWithThrow<T, R> f) {
return t -> {
try {return f.apply(t);
} catch (Exception ex) {return SneakilyThrowException.sneakyThrow(ex);
}
};
}
最初,咱们就能够在代码中优雅的应用了:
public static void sneakyThrowFinal(){
try {Stream.of(new MyStudents()).map(SneakilyThrowException.unchecked(MyStudents::changeAgeWithCheckedException)).toList();}catch (Exception e){System.out.println("get exception");
}
}
总结
以上就是如何在 lambda 表达式中优雅的进行异样转换的例子了。大家应用的过程中肯定要留神最初对异样的捕捉。
好了,本文的代码:
本文的例子 https://github.com/ddean2009/learn-java-base-9-to-20/tree/master/lambda-and-checked-exception/
更多文章请看 www.flydean.com