如何从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
。
答案 0 :(得分:225)
你的问题的简单答案是:你不能,至少不能直接。这不是你的错。 Oracle搞砸了。他们坚持检查异常的概念,但在设计功能接口,流,lambda等时不一致忘记处理已检查的异常。这对于像专家这样的专家来说是很重要的Robert C. Martin称检查异常为失败的实验。
在我看来,这是 API 中的一个巨大的 bug 以及语言规范中的一个小错误。
API中的错误是它没有提供转发已检查异常的工具,这实际上会对函数式编程产生很大的意义。正如我将在下面演示的那样,这样的设施很容易实现。
语言规范中的错误是,只要类型参数仅用于允许类型列表的情况下({{ 1}}子句)。
我们作为Java程序员的期望是以下代码应该编译:
throws
然而,它给出了:
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 E
是否也是如此。< / p>
缺少的是用于传递已检查异常的类型参数的声明。以下代码显示了如何使用当前语法声明这样的传递类型参数。除了标记行中的特殊情况(这是下面讨论的限制)之外,此代码将按预期编译和运行。
Stream.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
和super
ClassNotFoundException
的共同IOException
。
需要对类型推断的定义进行调整,以便如果在允许类型列表的情况下使用type参数(Exception
子句),编译器将查找多个类型。然后编译器报告的异常类型将与引用方法的已检查异常的原始throws
声明一样具体,而不是单个catch-all超类型。
坏消息是,这意味着甲骨文搞砸了。当然它们不会破坏用户域代码,但是将异常类型参数引入现有的功能接口会破坏显式使用这些接口的所有用户域代码的编译。他们必须发明一些新的语法糖来解决这个问题。
更糟糕的消息是,Brian Goetz在2010年已经讨论过这个话题https://blogs.oracle.com/briangoetz/entry/exception_transparency_in_java(新链接:http://mail.openjdk.java.net/pipermail/lambda-dev/2010-June/001484.html)但是我被告知这次调查最终没有成功,而且我知道Oracle目前没有工作来减轻已检查的异常和lambdas之间的相互作用。
答案 1 :(得分:161)
这个LambdaExceptionUtil
帮助程序类允许您在Java流中使用任何已检查的异常,如下所示:
Stream.of("java.lang.Object", "java.lang.Integer", "java.lang.String")
.map(rethrowFunction(Class::forName))
.collect(Collectors.toList());
注意Class::forName
抛出ClassNotFoundException
,已检查。流本身也抛出ClassNotFoundException
,而不是包含未经检查的异常。
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");
}
注1:上述rethrow
课程的LambdaExceptionUtil
方法可以毫无顾虑地使用,可以在任何情况下使用 。非常感谢用户@PaoloC帮助解决了最后一个问题:现在编译器会要求你添加throw子句和所有内容,好像你可以在Java 8流上本地抛出已检查的异常。
注2:以上uncheck
课程的LambdaExceptionUtil
方法是奖励方法,如果您不想要,可以安全地从课程中删除它们使用它们。如果您确实使用过它们,请小心操作,而不是在了解以下用例,优点/缺点和限制之前:
•如果要调用的方法实际上永远不会抛出它声明的异常,则可以使用uncheck
方法。例如:new String(byteArr,&#34; UTF-8&#34;)抛出UnsupportedEncodingException,但Java规范保证UTF-8始终存在。在这里,投掷声明是一个令人讨厌的问题,任何解决方案,以最小的样板沉默它是受欢迎的:String text = uncheck(() -> new String(byteArr, "UTF-8"));
•如果您正在实施一个严格的界面,您可以使用uncheck
方法,而您没有选择添加投掷声明,但抛出异常是完全合适的。为了获得抛出它的特权而包装异常会导致带有虚假异常的堆栈跟踪,这些异常不会导致实际出错的信息。一个很好的例子是Runnable.run(),它不会抛出任何已检查的异常。
•无论如何,如果您决定使用uncheck
方法,
请注意抛出CHECKED异常而没有throws子句的这两个后果:1)调用代码无法通过名称捕获它(如果你尝试,编译器会说:异常永远不会抛出体内相应的尝试声明)。它会冒泡并可能被一些&#34; catch异常&#34;捕获在主程序循环中。或者&#34;抓住Throwable&#34;,这可能是你想要的。 2)它违反了最不令人意外的原则:抓住RuntimeException
以便能够保证捕获所有可能的异常将不再足够。出于这个原因,我认为这不应该在框架代码中完成,而只能在您完全控制的业务代码中完成。
答案 2 :(得分:23)
你无法安全地做到这一点。你可以作弊,但是你的程序被打破了,这将不可避免地回来咬人(应该是你,但通常我们的作弊会在其他人身上爆炸。)
这是一种稍微安全的方法(但我仍然不建议这样做。)
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中的异常,从流管道中抛出一个信号,指示计算异常失败,捕获信号,并对该信号进行操作以抛出基础异常。关键是你总是捕获合成异常,而不是在不声明抛出异常的情况下允许检查异常泄漏。
答案 3 :(得分:22)
你可以!
扩展@marcg&#39; UtilException
并在必要时添加throw E
:这样,编译器会要求您添加throw子句以及所有内容&#39;就像你可以在java 8的流中抛出 natively 一样。
说明:只需在IDE中复制/粘贴LambdaExceptionUtil
,然后按照下面LambdaExceptionUtilTest
所示使用它。
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());
}
}
答案 4 :(得分:12)
只需使用NoException(我的项目),jOOλ's Unchecked,throwing-lambdas,Throwable interfaces或Faux Pas中的任何一个。
// 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));
答案 5 :(得分:6)
我编写了a library扩展了Stream 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());
}
答案 6 :(得分:6)
这个答案类似于17,但避免了包装器异常定义:
AWSSNS *publishCall = [AWSSNS defaultSNS];
AWSSNSPublishInput *message = [AWSSNSPublishInput new];
message.subject = @"My First Message";
//This is the ending point
message.targetArn = @"arn:aws:sns:us-west-2:895047407854:endpoint/APNS_SANDBOX/XXXXX/XXXX-XXXX-XXX-XXXX";
message.subject =@"hello";
message.message =@"teste from device";
// message.messageAttributes = messageDict;
//
// message.messageStructure = jsonString;
[publishCall publish:message completionHandler:^(AWSSNSPublishResponse * _Nullable response, NSError * _Nullable error) {
if(error) NSLog(@"%@", [error userInfo]);
if(response) NSLog(@"%@", [response description]);
}];
答案 7 :(得分:5)
你不能。
但是,您可能需要查看one of my projects,这样您就可以更轻松地操纵这种“抛出lambdas”。
在您的情况下,您可以这样做:
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()
一些默认值。
请注意,这仍然是一项正在进行的工作,更多的是即将到来。更好的名字,更多的功能等。
答案 8 :(得分:3)
总结上面的注释,高级解决方案是使用一个特殊的包装器,用于未经检查的函数,使用类似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());
下面的代码演示了消费者,供应商和功能界面。它可以很容易地扩展。此示例删除了一些公共关键字。
类尝试是客户端代码的端点。安全方法可能具有每种功能类型的唯一名称。 CheckedConsumer , CheckedSupplier 和 CheckedFunction 是lib功能的检查类似物,可以独立使用尝试
CheckedBuilder 是在某些已检查函数中处理异常的接口。如果上一次失败, orTry 允许执行另一个相同类型的函数。 handle 提供异常处理,包括异常类型过滤。处理程序的顺序很重要。减少方法不安全和重新抛出重新抛出执行链中的最后一个异常。如果所有函数都失败,则减少方法 orElse 和 orElseGet 返回备用值,如可选值。还有方法抑制。 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);
}
}
答案 9 :(得分:2)
TL; DR只需使用Lombok的@SneakyThrows
。
Christian Hujer已经详细解释了为什么严格地说,由于Java的限制,为什么不能从流中抛出检查异常。
其他一些答案已经解释了一些技巧,可以绕开语言的局限性,但仍然能够满足抛出“受检查的异常本身,而不必在流中添加难看的try / catching”的要求。,其中一些需要额外的数十行样板。
我将着重强调这样做的另一种选择,即恕我直言要比其他所有方法都干净得多: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
中,而无需显式声明。
答案 10 :(得分:0)
如果您使用Spring Framework,则可以使用https://docs.spring.io/spring/docs/5.2.8.RELEASE/javadoc-api/org/springframework/util/ReflectionUtils.html#rethrowRuntimeException-java.lang.Throwable-此处所述的ReflectionUtils.rethrowRuntimeException(ex)
该util方法的优点在于它会重新引发与Runtime类型完全相同的异常,因此,您期望catch类型异常的catch块仍会按预期捕获它。
答案 11 :(得分:0)
这里是原始问题的不同视图或解决方案。在这里,我展示了我们可以选择编写仅处理值的有效子集的代码,并具有在引发异常时检测和处理案例的选项。
@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);
}
答案 12 :(得分:0)
您还可以编写包装器方法来包装未检查的异常,甚至可以使用表示另一个功能接口(具有相同的返回类型 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;
}
答案 13 :(得分:0)
可能更好,更实用的方法是包装异常并在流中进一步传播它们。例如,请查看Try类型的Vavr。
示例:
@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);
}
};
}
OR
RuntimeException
第二次实现避免将异常包装在throwUnchecked
中。 report = "On {} there are 300 {} wind turbines, these lies in {}".format(site, turbine, country)
有效,因为几乎所有的通用异常在java中都被视为未选中。
答案 14 :(得分:0)
我使用这种包装异常:
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();
}
}
虽然在第一次rethrow()
调用期间无论如何都会重新抛出异常(哦,Java泛型......),这种方式允许对可能的异常进行严格的静态定义(要求在{{1}中声明它们}})。并且不需要throws
或其他东西。
答案 15 :(得分:0)
处理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
引发异常,并将其转换为Function
,返回已完成的CompletableFuture
- 正常完成原始函数的结果,或者在抛出异常时异常完成。
第二个map
操作说明您现在获得Stream<CompletableFuture<T>>
而非Stream<T>
。如果上游操作成功,CompletableFuture
仅负责执行此操作。 API使这个显而易见,但相对无痛。
直到你进入collect
阶段,就是这样。这是我们需要一个非常重要的帮助方法的地方。我们希望&#34;提升&#34;正常的收集操作(在这种情况下,toList()
)&#34;内部&#34; CompletableFuture
- cfCollector()
允许我们使用不需要的supplier
,accumulator
,combiner
和finisher
来实现这一点了解CompletableFuture
的所有内容。
辅助方法可以在我的MonadUtils
课程的GitHub上找到,这仍然是一项正在进行中的工作。
答案 16 :(得分:0)
我同意上面的评论,在使用Stream.map时,你只能实现不抛出异常的函数。
然而,你可以创建自己的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");
}
}
答案 17 :(得分:-1)
我认为这种方法是正确的:
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,只是回到一个好的旧循环,除非你正在使用并行流并且paralellization带来客观的好处,证明了不可读性。代码。
正如许多其他人所指出的,这种情况有解决方案,我希望其中一个能够成为未来的Java版本。