我如何从Java 8流内部抛出CHECKED异常?

我如何从Java 8流/lambdas内部抛出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/catches

148507 次浏览

这个LambdaExceptionUtil帮助类允许你在Java流中使用任何受控异常,如下所示:

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());

注意Class::forName抛出ClassNotFoundException,即检查。流本身也会抛出ClassNotFoundException和NOT一些未检查的包装异常。

public final 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; }


}

关于如何使用它的许多其他示例(在静态导入LambdaExceptionUtil之后):

@Test
public void test_Consumer_with_checked_exceptions() throws IllegalAccessException {
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));
}


@Test
public void test_Function_with_checked_exceptions() throws ClassNotFoundException {
List<Class> classes1
= Stream.of("Object", "Integer", "String")
.map(rethrowFunction(className -> Class.forName("java.lang." + className)))
.collect(Collectors.toList());


List<Class> classes2
= Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
}


@Test
public void test_Supplier_with_checked_exceptions() throws ClassNotFoundException {
Collector.of(
rethrowSupplier(() -> new StringJoiner(new String(new byte[]{77, 97, 114, 107}, "UTF-8"))),
StringJoiner::add, StringJoiner::merge, StringJoiner::toString);
}


@Test
public void test_uncheck_exception_thrown_by_method() {
Class clazz1 = uncheck(() -> Class.forName("java.lang.String"));


Class clazz2 = uncheck(Class::forName, "java.lang.String");
}


@Test (expected = ClassNotFoundException.class)
public void test_if_correct_exception_is_still_thrown_by_method() {
Class clazz3 = uncheck(Class::forName, "INVALID");
}

截至2015年11月更新在@PaoloC, 请在下面检查他的回答并投票的帮助下改进了代码。他帮助解决了最后一个问题:现在编译器会要求您添加抛出子句,一切就好像您可以在Java 8流上本机抛出检查异常一样。


上面的LambdaExceptionUtil类的rethrow方法可以毫无顾虑地使用,并且是可以在任何情况下使用


上面的LambdaExceptionUtil类的uncheck方法是额外的方法,如果你不想使用它们,可以安全地从类中删除它们。如果你确实使用了它们,请谨慎使用,在了解以下用例、优点/缺点和限制之前不要使用:

•如果你调用的方法字面上永远不会抛出它声明的异常,你可以使用uncheck方法。例如:new String(byteArr, "UTF-8")抛出UnsupportedEncodingException,但Java规范保证UTF-8始终存在。在这里,throws声明是一个麻烦,任何用最少的样板文件来沉默它的解决方案都是受欢迎的:String text = uncheck(() -> new String(byteArr, "UTF-8"));

•如果你正在实现一个严格的接口,你没有添加throws声明的选项,但抛出异常是完全合适的,你可以使用uncheck方法。包装一个异常只是为了获得抛出它的特权,结果是一个带有虚假异常的堆栈跟踪,这些异常没有提供关于实际错误的信息。一个很好的例子是Runnable.run(),它不会抛出任何受控异常。

在任何情况下,如果你决定使用uncheck方法, 请注意在没有throws子句的情况下抛出CHECKED异常的两个后果:1)调用代码将无法通过名称捕获它(如果你尝试,编译器会说:异常从未在相应的try语句体中抛出)。它将冒泡,并可能在主程序循环中被一些“捕获异常”捕获。或者“catch throwable”,这可能就是你想要的。2)它违反了最小意外原则:它将不再足以捕获RuntimeException,以能够保证捕获所有可能的异常。基于这个原因,我认为这不应该在框架代码中完成,而应该在你完全控制的业务代码中完成
  • 引用:

这样做是不安全的。你可以作弊,但你的程序坏了,这将不可避免地反过来伤害别人(应该是你,但我们的作弊经常会伤害到别人)。

这里有一个稍微安全一点的方法(但我仍然不推荐这样做)。

class WrappedException extends RuntimeException {
Throwable cause;


WrappedException(Throwable cause) { this.cause = cause; }
}


static WrappedException throwWrapped(Throwable t) {
throw new WrappedException(t);
}


try
source.stream()
.filter(e -> { ... try { ... } catch (IOException e) { throwWrapped(e); } ... })
...
}
catch (WrappedException w) {
throw (IOException) w.cause;
}

在这里,您要做的是捕获lambda中的异常,从流管道中抛出一个信号,表明计算异常失败,捕获信号,并对该信号进行操作以抛出底层异常。关键是您总是捕获合成异常,而不是在没有声明抛出异常的情况下允许已检查异常泄漏。

这不是你的错。甲骨文搞砸了。他们坚持受控异常的概念,但在设计功能接口、流、lambda等时,却总是忘记照顾受控异常。这对罗伯特·c·马丁(Robert C. Martin)等专家来说都是好消息,他把受控异常称为失败的实验。

在我看来,这是一个巨大的错误API和一个小错误在语言规范

API中的缺陷在于,它没有提供转发已检查异常的功能,而这实际上对函数式编程非常有意义。正如我将在下面演示的那样,这样的设施是很容易实现的。

语言规范中的错误在于,它不允许类型参数推断类型列表而不是单个类型,只要类型参数仅用于允许类型列表的情况(throws子句)。

作为Java程序员,我们的期望是编译以下代码:

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;


public class CheckedStream {
// List variant to demonstrate what we actually had before refactoring.
public List<Class> getClasses(final List<String> names) throws ClassNotFoundException {
final List<Class> classes = new ArrayList<>();
for (final String name : names)
classes.add(Class.forName(name));
return classes;
}


// The Stream function which we want to compile.
public Stream<Class> getClasses(final Stream<String> names) throws ClassNotFoundException {
return names.map(Class::forName);
}
}

然而,它给出:

cher@armor1:~/playground/Java/checkedStream$ javac CheckedStream.java
CheckedStream.java:13: error: incompatible thrown types ClassNotFoundException in method reference
return names.map(Class::forName);
^
1 error

函数接口的定义方式目前阻止编译器转发异常——没有声明告诉Stream.map()如果Function.apply() throws EStream.map() throws E也是。

缺少的是用于传递已检查异常的类型参数的声明。下面的代码展示了如何使用当前语法声明这种传递类型参数。除了标记行中的特殊情况(这是下面讨论的限制),此代码将按预期进行编译和运行。

import java.io.IOException;
interface Function<T, R, E extends Throwable> {
// Declare you throw E, whatever that is.
R apply(T t) throws E;
}


interface Stream<T> {
// Pass through E, whatever mapper defined for E.
<R, E extends Throwable> Stream<R> map(Function<? super T, ? extends R, E> mapper) throws E;
}


class Main {
public static void main(final String... args) throws ClassNotFoundException {
final Stream<String> s = null;


// Works: E is ClassNotFoundException.
s.map(Class::forName);


// Works: E is RuntimeException (probably).
s.map(Main::convertClass);


// Works: E is ClassNotFoundException.
s.map(Main::throwSome);


// Doesn't work: E is Exception.
s.map(Main::throwSomeMore);  // error: unreported exception Exception; must be caught or declared to be thrown
}
    

public static Class convertClass(final String s) {
return Main.class;
}


static class FooException extends ClassNotFoundException {}


static class BarException extends ClassNotFoundException {}


public static Class throwSome(final String s) throws FooException, BarException {
throw new FooException();
}


public static Class throwSomeMore(final String s) throws ClassNotFoundException, IOException  {
throw new FooException();
}
}

throwSomeMore的情况下,我们希望看到IOException被遗漏,但它实际上遗漏了Exception

这并不完美,因为类型推断似乎是在寻找单一类型,即使在异常的情况下也是如此。因为类型推断需要一个单一类型,所以E需要解析为ClassNotFoundExceptionIOException的公共super,即Exception

需要对类型推断的定义进行调整,以便在允许使用类型列表(throws子句)时使用类型形参,编译器将查找多个类型。然后,编译器报告的异常类型将与引用方法的检查异常的原始throws声明一样具体,而不是一个单一的捕捉所有的超类型。

坏消息是,这意味着甲骨文搞砸了。当然,它们不会破坏用户土地代码,但是向现有功能接口引入异常类型参数会破坏显式使用这些接口的所有用户土地代码的编译。他们必须发明一些新的语法来解决这个问题。

更糟糕的消息是,Brian Goetz在2010年(https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_javahttp://mail.openjdk.java.net/pipermail/lambda-dev/2010-June/001484.html)已经讨论过这个话题,但我被告知这个调查最终没有成功,而且据我所知,Oracle目前没有减轻检查异常和lambdas之间交互的工作。

你不能。

然而,你可能想要看看我的一个项目,它可以让你更容易地操纵这样的“抛出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());

和catch 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");


}


}

你可以!

扩展@marcg的UtilException并在必要的地方添加throw E:这样,编译器会要求你添加throw子句和其他一切就好像你可以在java 8的流上抛出检查异常本机一样。

说明:只需复制/粘贴LambdaExceptionUtil在你的IDE,然后使用它如下所示的LambdaExceptionUtilTest. xml。

public final class LambdaExceptionUtil {


@FunctionalInterface
public interface Consumer_WithExceptions<T, E extends Exception> {
void accept(T t) throws E;
}


@FunctionalInterface
public interface Function_WithExceptions<T, R, E extends Exception> {
R apply(T t) throws E;
}


/**
* .forEach(rethrowConsumer(name -> System.out.println(Class.forName(name))));
*/
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) {
throwActualException(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) {
throwActualException(exception);
return null;
}
};
}


@SuppressWarnings("unchecked")
private static <E extends Exception> void throwActualException(Exception exception) throws E {
throw (E) exception;
}


}

一些测试显示使用和行为:

public class LambdaExceptionUtilTest {


@Test(expected = MyTestException.class)
public void testConsumer() throws MyTestException {
Stream.of((String)null).forEach(rethrowConsumer(s -> checkValue(s)));
}


private void checkValue(String value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
}


private class MyTestException extends Exception { }


@Test
public void testConsumerRaisingExceptionInTheMiddle() {
MyLongAccumulator accumulator = new MyLongAccumulator();
try {
Stream.of(2L, 3L, 4L, null, 5L).forEach(rethrowConsumer(s -> accumulator.add(s)));
fail();
} catch (MyTestException e) {
assertEquals(9L, accumulator.acc);
}
}


private class MyLongAccumulator {
private long acc = 0;
public void add(Long value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
acc += value;
}
}


@Test
public void testFunction() throws MyTestException {
List<Integer> sizes = Stream.of("ciao", "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
assertEquals(2, sizes.size());
assertEquals(4, sizes.get(0).intValue());
assertEquals(5, sizes.get(1).intValue());
}


private Integer transform(String value) throws MyTestException {
if(value==null) {
throw new MyTestException();
}
return value.length();
}


@Test(expected = MyTestException.class)
public void testFunctionRaisingException() throws MyTestException {
Stream.of("ciao", null, "hello").<Integer>map(rethrowFunction(s -> transform(s))).collect(toList());
}


}

我写了一个图书馆,它扩展了流API,允许你抛出检查异常。它使用了Brian Goetz的技巧。

您的代码将变成

public List<Class> getClasses() throws ClassNotFoundException {
Stream<String> classNames =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String");


return ThrowingStream.of(classNames, ClassNotFoundException.class)
.map(Class::forName)
.collect(Collectors.toList());
}

处理map操作抛出的受控异常的唯一内置方法是将它们封装在CompletableFuture中。(如果你不需要保存异常,Optional是一个更简单的选择。)这些类旨在允许您以函数的方式表示偶然操作。

需要几个重要的帮助器方法,但是你可以得到相对简洁的代码,同时仍然明显地表明你的流的结果取决于map操作是否成功完成。这是它的样子:

    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方法接受一个引发异常的Function,并将其转换为一个返回已经完成的CompletableFutureFunction——要么用原始函数的结果正常完成,要么用抛出的异常异常完成。

第二个map操作说明你现在得到了一个Stream<CompletableFuture<T>>而不仅仅是Stream<T>CompletableFuture只负责在上游操作成功时执行该操作。API可以显式地做到这一点,但相对来说并不痛苦。

直到你到达collect阶段。这就是我们需要一个非常重要的助手方法的地方。我们想要“提升”一个正常的收集操作(在本例中为toList())CompletableFuture——cfCollector()的“内部”让我们使用supplieraccumulatorcombinerfinisher来做这件事,它们不需要知道关于CompletableFuture的任何事情。

helper方法可以在GitHub上的MonadUtils类中找到,这在很大程度上仍然是一项正在进行的工作。

综上所述,高级解决方案为未检查的函数使用特殊的包装器,如API,它提供恢复、重抛出和抑制功能。

Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(Try.<String, Class<?>>safe(Class::forName)
.handle(System.out::println)
.unsafe())
.collect(toList());

下面的代码演示了消费者、供应商和函数接口。它可以很容易地扩展。本例删除了一些公共关键字。

试一试类是客户端代码的端点。安全方法对于每个函数类型都可以有唯一的名称。 CheckedConsumerCheckedSupplierCheckedFunction是lib函数的checked模拟,可以独立于试一试使用

CheckedBuilder是处理某些已检查函数中的异常的接口。orTry允许执行另一个相同类型的函数,如果前一个失败。处理提供异常处理,包括异常类型筛选。处理程序的顺序很重要。Reduce方法不安全的重新抛出重新抛出执行链中的最后一个异常。如果所有函数都失败,Reduce方法orElseorElseGet返回可选值。还有方法抑制CheckedWrapper是CheckedBuilder的通用实现。

final class Try {


public static <T> CheckedBuilder<Supplier<T>, CheckedSupplier<T>, T>
safe(CheckedSupplier<T> supplier) {
return new CheckedWrapper<>(supplier,
(current, next, handler, orResult) -> () -> {
try { return current.get(); } catch (Exception ex) {
handler.accept(ex);
return next.isPresent() ? next.get().get() : orResult.apply(ex);
}
});
}


public static <T> Supplier<T> unsafe(CheckedSupplier<T> supplier) {
return supplier;
}


public static <T> CheckedBuilder<Consumer<T>, CheckedConsumer<T>, Void>
safe(CheckedConsumer<T> consumer) {
return new CheckedWrapper<>(consumer,
(current, next, handler, orResult) -> t -> {
try { current.accept(t); } catch (Exception ex) {
handler.accept(ex);
if (next.isPresent()) {
next.get().accept(t);
} else {
orResult.apply(ex);
}
}
});
}


public static <T> Consumer<T> unsafe(CheckedConsumer<T> consumer) {
return consumer;
}


public static <T, R> CheckedBuilder<Function<T, R>, CheckedFunction<T, R>, R>
safe(CheckedFunction<T, R> function) {
return new CheckedWrapper<>(function,
(current, next, handler, orResult) -> t -> {
try { return current.applyUnsafe(t); } catch (Exception ex) {
handler.accept(ex);
return next.isPresent() ? next.get().apply(t) : orResult.apply(ex);
}
});
}


public static <T, R> Function<T, R> unsafe(CheckedFunction<T, R> function) {
return function;
}


@SuppressWarnings ("unchecked")
static <T, E extends Throwable> T throwAsUnchecked(Throwable exception) throws E {
throw (E) exception;
}
}


@FunctionalInterface interface CheckedConsumer<T> extends Consumer<T> {
void acceptUnsafe(T t) throws Exception;
@Override default void accept(T t) {
try { acceptUnsafe(t); } catch (Exception ex) {
Try.throwAsUnchecked(ex);
}
}
}


@FunctionalInterface interface CheckedFunction<T, R> extends Function<T, R> {
R applyUnsafe(T t) throws Exception;
@Override default R apply(T t) {
try { return applyUnsafe(t); } catch (Exception ex) {
return Try.throwAsUnchecked(ex);
}
}
}


@FunctionalInterface interface CheckedSupplier<T> extends Supplier<T> {
T getUnsafe() throws Exception;
@Override default T get() {
try { return getUnsafe(); } catch (Exception ex) {
return Try.throwAsUnchecked(ex);
}
}
}


interface ReduceFunction<TSafe, TUnsafe, R> {
TSafe wrap(TUnsafe current, Optional<TSafe> next,
Consumer<Throwable> handler, Function<Throwable, R> orResult);
}


interface CheckedBuilder<TSafe, TUnsafe, R> {
CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next);


CheckedBuilder<TSafe, TUnsafe, R> handle(Consumer<Throwable> handler);


<E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handle(
Class<E> exceptionType, Consumer<E> handler);


CheckedBuilder<TSafe, TUnsafe, R> handleLast(Consumer<Throwable> handler);


<E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R> handleLast(
Class<E> exceptionType, Consumer<? super E> handler);


TSafe unsafe();
TSafe rethrow(Function<Throwable, Exception> transformer);
TSafe suppress();
TSafe orElse(R value);
TSafe orElseGet(Supplier<R> valueProvider);
}


final class CheckedWrapper<TSafe, TUnsafe, R>
implements CheckedBuilder<TSafe, TUnsafe, R> {


private final TUnsafe function;
private final ReduceFunction<TSafe, TUnsafe, R> reduceFunction;


private final CheckedWrapper<TSafe, TUnsafe, R> root;
private CheckedWrapper<TSafe, TUnsafe, R> next;


private Consumer<Throwable> handlers = ex -> { };
private Consumer<Throwable> lastHandlers = ex -> { };


CheckedWrapper(TUnsafe function,
ReduceFunction<TSafe, TUnsafe, R> reduceFunction) {
this.function = function;
this.reduceFunction = reduceFunction;
this.root = this;
}


private CheckedWrapper(TUnsafe function,
CheckedWrapper<TSafe, TUnsafe, R> prev) {
this.function = function;
this.reduceFunction = prev.reduceFunction;
this.root = prev.root;
prev.next = this;
}


@Override public CheckedBuilder<TSafe, TUnsafe, R> orTry(TUnsafe next) {
return new CheckedWrapper<>(next, this);
}


@Override public CheckedBuilder<TSafe, TUnsafe, R> handle(
Consumer<Throwable> handler) {
handlers = handlers.andThen(handler);
return this;
}


@Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R>
handle(Class<E> exceptionType, Consumer<E> handler) {
handlers = handlers.andThen(ex -> {
if (exceptionType.isInstance(ex)) {
handler.accept(exceptionType.cast(ex));
}
});
return this;
}


@Override public CheckedBuilder<TSafe, TUnsafe, R> handleLast(
Consumer<Throwable> handler) {
lastHandlers = lastHandlers.andThen(handler);
return this;
}


@Override public <E extends Throwable> CheckedBuilder<TSafe, TUnsafe, R>
handleLast(Class<E> exceptionType, Consumer<? super E> handler) {
lastHandlers = lastHandlers.andThen(ex -> {
if (exceptionType.isInstance(ex)) {
handler.accept(exceptionType.cast(ex));
}
});
return this;
}


@Override public TSafe unsafe() {
return root.reduce(ex -> Try.throwAsUnchecked(ex));
}


@Override
public TSafe rethrow(Function<Throwable, Exception> transformer) {
return root.reduce(ex -> Try.throwAsUnchecked(transformer.apply(ex)));
}


@Override public TSafe suppress() {
return root.reduce(ex -> null);
}


@Override public TSafe orElse(R value) {
return root.reduce(ex -> value);
}


@Override public TSafe orElseGet(Supplier<R> valueProvider) {
Objects.requireNonNull(valueProvider);
return root.reduce(ex -> valueProvider.get());
}


private TSafe reduce(Function<Throwable, R> orResult) {
return reduceFunction.wrap(function,
Optional.ofNullable(next).map(p -> p.reduce(orResult)),
this::handle, orResult);
}


private void handle(Throwable ex) {
for (CheckedWrapper<TSafe, TUnsafe, R> current = this;
current != null;
current = current.next) {
current.handlers.accept(ex);
}
lastHandlers.accept(ex);
}
}

我使用这种包装异常:

public class CheckedExceptionWrapper extends RuntimeException {
...
public <T extends Exception> CheckedExceptionWrapper rethrow() throws T {
throw (T) getCause();
}
}

它需要静态处理这些异常:

void method() throws IOException, ServletException {
try {
list.stream().forEach(object -> {
...
throw new CheckedExceptionWrapper(e);
...
});
} catch (CheckedExceptionWrapper e){
e.<IOException>rethrow();
e.<ServletExcepion>rethrow();
}
}

<一个href = " https://tio.run/ # # lVNNb9swDL37VxA@yUDjH9BkAYagwwpsHbAG6IBhB1VWbaWyZEh00mDwX58n@SPxXPkwXgSJ5HuPpHigR7o6ZK@tKCttEA7ungqd3n@7e2O8QqHVOpr6ahQy / WgMPduA44uwuI6iqn6WggGT1Fr4SoWC3xE4G94tUnTHUYsMSuclj2iEyn @AmpymwAWRp8sXBQM2d4UP3WAJElLjoXOSLLunE3UdgxGHCnyAXuIGAEnRd3Azot70PhJ1yoLUflaNr2yLUh3gQ / QF55S650k / syl1PENxE / ayCwepHhDc55AefMIqUXDaenEv2hzR1lBLKy2s8Bw@midbJ / @QEtO7IRytAYYRVYAWaiQJwvQXZe6Du8Kzl75NefJ0KrihvAQ3T8vzSTiKiQM917J49kiL1NdY@oGqVAqEjNa5wXCqcvJgI8g8UxLKNfwriY3wens55k83Uy82yGJ / B9@uNkBqnBgiLUZPnZ3DDvTr9RiQ9@Qq8zC91qhKHnoVw8ruDThvRdBnyV3s6vtuwHZ2gf1rqnQOfxmf9FyLXFR9qX2cVH3M9r@Z5J9AjnHnWcP9alt2j/sRdLctqsf0o3otlasp/wL" rel="nofollow noreferrer" title="Java (JDK) -试试它在线">试试它在线!< / >

尽管异常无论如何都会在第一次rethrow()调用时被重新抛出(哦,Java泛型…),这种方式允许获得可能异常的严格静态定义(需要在throws中声明它们)。并且不需要instanceof之类的东西。

这个答案类似于17,但避免了包装器异常定义:

List test = new ArrayList();
try {
test.forEach(obj -> {


//let say some functionality throws an exception
try {
throw new IOException("test");
}
catch(Exception e) {
throw new RuntimeException(e);
}
});
}
catch (RuntimeException re) {
if(re.getCause() instanceof IOException) {
//do your logic for catching checked
}
else
throw re; // it might be that there is real runtime exception
}

只要使用任何一个NoException(我的项目),jOOλ的无节制的throwing-lambdasThrowable接口,或失礼

// NoException
stream.map(Exceptions.sneak().function(Class::forName));


// jOOλ
stream.map(Unchecked.function(Class::forName));


// throwing-lambdas
stream.map(Throwing.function(Class::forName).sneakyThrow());


// Throwable interfaces
stream.map(FunctionWithThrowable.aFunctionThatUnsafelyThrowsUnchecked(Class::forName));


// Faux Pas
stream.map(FauxPas.throwingFunction(Class::forName));

我认为这种方法是正确的:

public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes;
try {
classes = Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String").map(className -> {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new UndeclaredThrowableException(e);
}
}).collect(Collectors.toList());
} catch (UndeclaredThrowableException e) {
if (e.getCause() instanceof ClassNotFoundException) {
throw (ClassNotFoundException) e.getCause();
} else {
// this should never happen
throw new IllegalStateException(e.getMessage(), e);
}
}
return classes;
}

将检查异常包装在Callable中的UndeclaredThrowableException(这是该异常的用例)中,并在外部将其展开。

是的,我觉得它很难看,我建议在这种情况下不要使用lambdas,而是退回到一个好的旧循环,除非您正在使用并行流,并且并行化带来了客观的好处,可以证明代码的不可读性是正确的。

正如许多人所指出的那样,有解决这种情况的解决方案,我希望其中一个解决方案能够出现在Java的未来版本中。

也许,更好、更实用的方法是包装异常并在流中进一步传播它们。例如,看看Vavr试一试类型。

例子:

interface CheckedFunction<I, O> {
O apply(I i) throws Exception; }


static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
return i -> {
try {
return f.apply(i);
} catch(Exception ex) {


throw new RuntimeException(ex);
}
} }


fileNamesToRead.map(unchecked(file -> Files.readAllLines(file)))

@SuppressWarnings("unchecked")
private static <T, E extends Exception> T throwUnchecked(Exception e) throws E {
throw (E) e;
}


static <I, O> Function<I, O> unchecked(CheckedFunction<I, O> f) {
return arg -> {
try {
return f.apply(arg);
} catch(Exception ex) {
return throwUnchecked(ex);
}
};
}

第二个实现避免将异常包装在RuntimeException中。throwUnchecked可以工作,因为在java中几乎所有的泛型异常都被视为未检查的。

只要使用Lombok的@SneakyThrows

Christian Hujer已经详细解释了为什么严格来说,由于Java的限制,从流中抛出受控异常是不可能的。

其他一些回答解释了一些技巧,可以绕过语言的限制,但仍然能够满足抛出“检查异常本身,并且不添加丑陋的try/ caught到流中”的要求,其中一些需要额外的数十行样板文件。

我将强调这样做的另一个选项,IMHO比所有其他选项都干净得多:Lombok的@SneakyThrows。这个问题在其他答案中也提到过,但有很多不必要的细节。

生成的代码非常简单:

public List<Class> getClasses() throws ClassNotFoundException {
List<Class> classes =
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(className -> getClass(className))
.collect(Collectors.toList());
return classes;
}


@SneakyThrows                                 // <= this is the only new code
private Class<?> getClass(String className) {
return Class.forName(className);
}

我们只需要一个Extract Method重构(由IDE完成)和一个附加行用于@SneakyThrows。注释负责添加所有样板文件,以确保您可以抛出检查异常,而无需将其包装在RuntimeException中,也无需显式声明它。

你也可以写一个包装器方法来包装未检查的异常,甚至用表示另一个功能接口的附加参数来增强包装器(具有相同的返回类型R)。 在这种情况下,您可以传递一个函数,该函数将在异常情况下执行并返回。 参见下面的例子:

private void run() {
List<String> list = Stream.of(1, 2, 3, 4).map(wrapper(i ->
String.valueOf(++i / 0), i -> String.valueOf(++i))).collect(Collectors.toList());
System.out.println(list.toString());
}


private <T, R, E extends Exception> Function<T, R> wrapper(ThrowingFunction<T, R, E> function,
Function<T, R> onException) {
return i -> {
try {
return function.apply(i);
} catch (ArithmeticException e) {
System.out.println("Exception: " + i);
return onException.apply(i);
} catch (Exception e) {
System.out.println("Other: " + i);
return onException.apply(i);
}
};
}


@FunctionalInterface
interface ThrowingFunction<T, R, E extends Exception> {
R apply(T t) throws E;
}

这是原来问题的不同观点或解决方案。在这里,我展示了我们有一个选项来编写只处理有效值子集的代码,并有一个选项来检测和处理抛出异常时的情况。

    @Test
public void getClasses() {


String[] classNames = {"java.lang.Object", "java.lang.Integer", "java.lang.Foo"};
List<Class> classes =
Stream.of(classNames)
.map(className -> {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
// log the error
return null;
}
})
.filter(c -> c != null)
.collect(Collectors.toList());


if (classes.size() != classNames.length) {
// add your error handling here if needed or process only the resulting list
System.out.println("Did not process all class names");
}


classes.forEach(System.out::println);
}

你可以用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;
}