我如何从Java 8 lambda内部抛出CHECKED异常,例如在流中使用?
换句话说,我想让代码像这样编译:
public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> Class.forName(className))
.collect(Collectors.toList());
return classes;
}
这段代码无法编译,因为上面的Class.forName()方法会抛出ClassNotFoundException,该异常会被检查。
请注意,我不想将已检查异常包装在运行时异常中,并抛出已包装的未检查异常。我想抛出检查异常本身,而不向流添加丑陋的try/catch。
处理map操作抛出的受控异常的唯一内置方法是将它们封装在CompletableFuture中。(如果你不需要保留异常,一个Optional是一个更简单的选择。)这些类旨在允许您以函数的方式表示偶然操作。
需要几个重要的帮助器方法,但是您可以得到相对简洁的代码,同时仍然明显地表明流的结果取决于映射操作是否成功完成。这是它的样子:
CompletableFuture<List<Class<?>>> classes =
Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double")
.map(MonadUtils.applyOrDie(Class::forName))
.map(cfc -> cfc.thenApply(Class::getSuperclass))
.collect(MonadUtils.cfCollector(ArrayList::new,
List::add,
(List<Class<?>> l1, List<Class<?>> l2) -> { l1.addAll(l2); return l1; },
x -> x));
classes.thenAccept(System.out::println)
.exceptionally(t -> { System.out.println("unable to get class: " + t); return null; });
这将产生以下输出:
[class java.lang.Object, class java.lang.Number, class java.lang.Number]
applyOrDie方法接受一个抛出异常的函数,并将其转换为一个返回已经完成的CompletableFuture的函数——要么用原始函数的结果正常完成,要么用抛出的异常异常完成。
第二个映射操作说明您现在得到了一个Stream<CompletableFuture<T>>,而不仅仅是一个Stream<T>。如果上游操作成功,CompletableFuture只负责执行该操作。API可以显式地做到这一点,但相对来说并不痛苦。
直到您到达收集阶段。这就是我们需要一个非常重要的助手方法的地方。我们希望“提升”一个正常的收集操作(在本例中是toList())在CompletableFuture——cfCollector()中,我们可以使用一个供应商、累加器、组合器和完成器来实现这一点,而不需要知道关于CompletableFuture的任何事情。
助手方法可以在GitHub上我的MonadUtils类中找到,这仍然是一项正在进行的工作。
处理map操作抛出的受控异常的唯一内置方法是将它们封装在CompletableFuture中。(如果你不需要保留异常,一个Optional是一个更简单的选择。)这些类旨在允许您以函数的方式表示偶然操作。
需要几个重要的帮助器方法,但是您可以得到相对简洁的代码,同时仍然明显地表明流的结果取决于映射操作是否成功完成。这是它的样子:
CompletableFuture<List<Class<?>>> classes =
Stream.of("java.lang.String", "java.lang.Integer", "java.lang.Double")
.map(MonadUtils.applyOrDie(Class::forName))
.map(cfc -> cfc.thenApply(Class::getSuperclass))
.collect(MonadUtils.cfCollector(ArrayList::new,
List::add,
(List<Class<?>> l1, List<Class<?>> l2) -> { l1.addAll(l2); return l1; },
x -> x));
classes.thenAccept(System.out::println)
.exceptionally(t -> { System.out.println("unable to get class: " + t); return null; });
这将产生以下输出:
[class java.lang.Object, class java.lang.Number, class java.lang.Number]
applyOrDie方法接受一个抛出异常的函数,并将其转换为一个返回已经完成的CompletableFuture的函数——要么用原始函数的结果正常完成,要么用抛出的异常异常完成。
第二个映射操作说明您现在得到了一个Stream<CompletableFuture<T>>,而不仅仅是一个Stream<T>。如果上游操作成功,CompletableFuture只负责执行该操作。API可以显式地做到这一点,但相对来说并不痛苦。
直到您到达收集阶段。这就是我们需要一个非常重要的助手方法的地方。我们希望“提升”一个正常的收集操作(在本例中是toList())在CompletableFuture——cfCollector()中,我们可以使用一个供应商、累加器、组合器和完成器来实现这一点,而不需要知道关于CompletableFuture的任何事情。
助手方法可以在GitHub上我的MonadUtils类中找到,这仍然是一项正在进行的工作。
你可以用apache commons-lang3库来实现。
https://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/function/Failable.html
public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(Failable.asFunction(Class::forName))
.collect(Collectors.toList());
return classes;
}
你不能。
然而,你可能想要看看我的一个项目,它可以让你更容易地操纵这种“投掷lambda”。
在你的情况下,你可以这样做:
import static com.github.fge.lambdas.functions.Functions.wrap;
final ThrowingFunction<String, Class<?>> f = wrap(Class::forName);
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(f.orThrow(MyException.class))
.collect(Collectors.toList());
并捕获MyException。
这是一个例子。另一个例子是你可以. orreturn()一些默认值。
请注意,这仍然是一项正在进行的工作,更多的是来。更好的名字,更多的功能等等。
在使用Stream时,我同意上面的评论。映射你被限制实现不抛出异常的函数。
然而,你可以创建自己的FunctionalInterface,抛出如下..
@FunctionalInterface
public interface UseInstance<T, X extends Throwable> {
void accept(T instance) throws X;
}
然后使用Lambdas或引用实现它,如下所示。
import java.io.FileWriter;
import java.io.IOException;
//lambda expressions and the execute around method (EAM) pattern to
//manage resources
public class FileWriterEAM {
private final FileWriter writer;
private FileWriterEAM(final String fileName) throws IOException {
writer = new FileWriter(fileName);
}
private void close() throws IOException {
System.out.println("close called automatically...");
writer.close();
}
public void writeStuff(final String message) throws IOException {
writer.write(message);
}
//...
public static void use(final String fileName, final UseInstance<FileWriterEAM, IOException> block) throws IOException {
final FileWriterEAM writerEAM = new FileWriterEAM(fileName);
try {
block.accept(writerEAM);
} finally {
writerEAM.close();
}
}
public static void main(final String[] args) throws IOException {
FileWriterEAM.use("eam.txt", writerEAM -> writerEAM.writeStuff("sweet"));
FileWriterEAM.use("eam2.txt", writerEAM -> {
writerEAM.writeStuff("how");
writerEAM.writeStuff("sweet");
});
FileWriterEAM.use("eam3.txt", FileWriterEAM::writeIt);
}
void writeIt() throws IOException{
this.writeStuff("How ");
this.writeStuff("sweet ");
this.writeStuff("it is");
}
}