Java 8 Lambda function that throws exception?
我知道如何创建对具有
1 |
但是,如果函数抛出异常,这不起作用,比如说定义为:
1 |
我该如何定义这个参考?
您需要执行以下操作之一。
-
如果它是您的代码,那么定义您自己的功能接口,声明已检查的异常:
1
2
3
4并使用它:
1void foo (CheckedFunction f) { ... } -
否则,在未声明已检查异常的方法中包装
Integer myMethod(String s) :1
2
3
4
5
6
7
8public Integer myWrappedMethod(String s) {
try {
return myMethod(s);
}
catch(IOException e) {
throw new UncheckedIOException(e);
}
}然后:
要么:
1
2
3
4
5
6
7
8
9Function<String, Integer> f =
(String t) -> {
try {
return myMethod(t);
}
catch(IOException e) {
throw new UncheckedIOException(e);
}
};
实际上,您可以使用Java 8的默认方法,使用处理异常的新接口扩展
考虑这个接口(extends
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @FunctionalInterface public interface ThrowingConsumer< T > extends Consumer< T > { @Override default void accept(final T elem) { try { acceptThrows(elem); } catch (final Exception e) { // Implement your own exception handling logic here.. // For example: System.out.println("handling an exception..."); // Or ... throw new RuntimeException(e); } } void acceptThrows(T elem) throws Exception; } |
然后,例如,如果您有一个列表:
1 |
如果你想使用一些抛出异常的代码来使用它(例如,用
1 2 3 4 5 6 7 8 9 |
但是使用这个新接口,您可以使用lambda表达式对其进行实例化,编译器不会抱怨:
1 2 3 4 5 | final ThrowingConsumer<String> throwingConsumer = aps -> { // maybe some other code here... throw new Exception("asdas"); }; list.forEach(throwingConsumer); |
或者甚至只是把它变得更简洁!:
1 2 3 4 | list.forEach((ThrowingConsumer<String>) aps -> { // maybe some other code here... throw new Exception("asda"); }); |
更新:看起来有一个非常好的Durian实用程序库部分名为Errors,它可以用来更灵活地解决这个问题。例如,在上面的实现中,我明确定义了错误处理策略(
样品用法:
1 | list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c))); |
我认为Durian的
- 将抛出函数包装到标准Java 8功能接口。
- 轻松指定处理错误的各种策略
- 在包装返回值的方法时,指定默认值或重新抛出RuntimeException之间存在重要区别。
-
抛出Java 8的功能接口版本
- 类似于fge的答案
-
用于抛出特定异常的标准接口
- 这解决了佐尔坦的担忧
要在项目中包含榴莲,您可以:
-
从
com.diffplug.durian:durian:3.3.0 的jcenter或maven中心抓住它 -
或者只是将两个小类粘贴到您的代码中:
Throwing.java 和Errors.java
这不是特定于Java 8.您正在尝试编译等效于:
1 2 3 4 5 6 | interface I { void m(); } class C implements I { public void m() throws Exception {} //can't compile } |
免责声明:我还没有使用过Java 8,只读过它。
1 2 3 4 5 6 7 8 | (String s) -> { try { return myMethod(s); } catch (IOException ex) { throw new RuntimeException(ex); // (Or do something else with it...) } } |
或者,如果您传递lambda的方法是您自己编写的方法,则可以定义新的功能接口并将其用作参数类型而不是
1 2 3 |
如果您不介意使用第三方库(Vavr),您可以写
1 |
它还有所谓的Try monad来处理错误:
1 2 3 | Try(() -> f.apply("test")) // results in a Success(Integer) or Failure(Throwable) .map(i -> ...) // only executed on Success ... |
请在这里阅读更多。
免责声明:我是Vavr的创始人。
你可以使用unthrow包装
1 |
要么
1 |
您可以。
扩展@marcg的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public final class LambdaExceptionUtil { @FunctionalInterface public interface Function_WithExceptions<T, R, E extends Exception> { R apply(T t) throws E; } /** * .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 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(); } private static class MyTestException extends Exception { } } |
但是,您可以创建自己的FunctionalInterface,如下所示抛出..
1 2 3 4 | @FunctionalInterface public interface UseInstance<T, X extends Throwable> { void accept(T instance) throws X; } |
然后使用Lambdas或引用实现它,如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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"); } } |
这里已经发布了很多很棒的回复。只是试图用不同的视角来解决问题。它只是我的2美分,请纠正我,如果我在某处错了。
FunctionalInterface中的Throws子句不是一个好主意
我认为由于以下原因,强制执行抛出IOException可能不是一个好主意
-
这对我来说就像Stream / Lambda的反模式。整个想法是调用者将决定提供什么代码以及如何处理异常。在许多情况下,IOException可能不适用于客户端。例如,如果客户端从缓存/内存获取值而不是执行实际I / O.
-
此外,流中的异常处理变得非常可怕。例如,如果我使用您的API,我的代码就会如此
1
2
3
4
5
6
7
8
9
10
11
12acceptMyMethod(s -> {
try {
Integer i = doSomeOperation(s);
return i;
} catch (IOException e) {
// try catch block because of throws clause
// in functional method, even though doSomeOperation
// might not be throwing any exception at all.
e.printStackTrace();
}
return null;
});丑陋不是吗?而且,正如我在第一点中提到的那样,doSomeOperation方法可能会抛出IOException(取决于客户端/调用者的实现),但由于FunctionalInterface方法中的throws子句,我总是要编写试着抓。
如果我真的知道这个API会抛出IOException,我该怎么办?
-
那么我们可能会将FunctionalInterface与典型的接口混淆。如果你知道这个API会抛出IOException,那么很可能你也知道一些默认/抽象行为。我认为您应该定义一个接口并部署您的库(使用默认/抽象实现),如下所示
1
2
3但是,客户端仍然存在try-catch问题。如果我在流中使用您的API,我仍然需要在hideous try-catch块中处理IOException。
-
提供默认的流友好API,如下所示
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17public interface MyAmazingAPI {
Integer myMethod(String s) throws IOException;
default Optional<Integer> myMethod(String s, Consumer<? super Exception> exceptionConsumer) {
try {
return Optional.ofNullable(this.myMethod(s));
} catch (Exception e) {
if (exceptionConsumer != null) {
exceptionConsumer.accept(e);
} else {
e.printStackTrace();
}
}
return Optional.empty();
}
}默认方法将使用者对象作为参数,它将负责处理异常。现在,从客户的角度来看,代码将如下所示
1
2
3strStream.map(str -> amazingAPIs.myMethod(str, Exception::printStackTrace))
.filter(Optional::isPresent)
.map(Optional::get).collect(toList());好吗?当然,可以使用记录器或其他处理逻辑来代替Exception :: printStackTrace。
-
您还可以公开类似于https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#exceptionally-java.util.function.Function-的方法。这意味着您可以公开另一个方法,该方法将包含先前方法调用的异常。缺点是你现在正在使你的API有状态,这意味着你需要处理线程安全,并最终成为性能损失。只是一个考虑的选择。
我在lambda中有Class.forName和Class.newInstance这个问题,所以我做了:
1 2 3 4 5 6 7 8 9 | public Object uncheckedNewInstanceForName (String name) { try { return Class.forName(name).newInstance(); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } |
在lambda中,而不是调用Class.forName("myClass")。newInstance()我刚刚调用了uncheckedNewInstanceForName("myClass")
这个问题一直困扰着我;这就是我创建这个项目的原因。
有了它你可以做:
1 |
JDK定义了39个接口,它们具有
并且当它们中的每一个扩展它们的非投掷对应物时,你也可以直接在lambdas中使用它们:
1 | myStringStream.map(f) // <-- works |
默认行为是当您抛出lambda抛出已检查的异常时,抛出
其他功能也可用。
使用Function包装器的另一个解决方案是返回结果包装器的实例,比如Success,如果一切顺利,则返回一个实例,比如Failure。
一些代码澄清事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | public interface ThrowableFunction<A, B> { B apply(A a) throws Exception; } public abstract class Try<A> { public static boolean isSuccess(Try tryy) { return tryy instanceof Success; } public static <A, B> Function<A, Try> tryOf(ThrowableFunction<A, B> function) { return a -> { try { B result = function.apply(a); return new Success(result); } catch (Exception e) { return new Failure<>(e); } }; } public abstract boolean isSuccess(); public boolean isError() { return !isSuccess(); } public abstract A getResult(); public abstract Exception getError(); } public class Success<A> extends Try<A> { private final A result; public Success(A result) { this.result = result; } @Override public boolean isSuccess() { return true; } @Override public A getResult() { return result; } @Override public Exception getError() { return new UnsupportedOperationException(); } @Override public boolean equals(Object that) { if(!(that instanceof Success)) { return false; } return Objects.equal(result, ((Success) that).getResult()); } } public class Failure<A> extends Try<A> { private final Exception exception; public Failure(Exception exception) { this.exception = exception; } @Override public boolean isSuccess() { return false; } @Override public A getResult() { throw new UnsupportedOperationException(); } @Override public Exception getError() { return exception; } } |
一个简单的用例:
1 2 3 | List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream(). map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))). collect(Collectors.toList()); |
偷偷摸摸的抛出习惯能够绕过Lambda表达式的
它可以用作Java集合中使用的
这是一个简单而改进的jib答案版本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | import static Throwing.rethrow; @Test public void testRethrow() { thrown.expect(IOException.class); thrown.expectMessage("i=3"); Arrays.asList(1, 2, 3).forEach(rethrow(e -> { int i = e.intValue(); if (i == 3) { throw new IOException("i=" + i); } })); } |
这只是将lambda包裹在一个重新抛出中。它使
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public final class Throwing { private Throwing() {} @Nonnull public static < T > Consumer< T > rethrow(@Nonnull final ThrowingConsumer< T > consumer) { return consumer; } /** * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it * allows the unchecked exception to propagate. * * http://www.baeldung.com/java-sneaky-throws */ @SuppressWarnings("unchecked") @Nonnull public static <E extends Throwable> void sneakyThrow(@Nonnull Throwable ex) throws E { throw (E) ex; } } |
在这里找到完整的代码和单元测试。
您可以使用ET。 ET是一个用于异常转换/转换的小型Java 8库。
使用ET它看起来像这样:
1 2 3 4 5 6 7 8 9 10 | // Do this once ExceptionTranslator et = ET.newConfiguration().done(); ... // if your method returns something Function<String, Integer> f = (t) -> et.withReturningTranslation(() -> myMethod(t)); // if your method returns nothing Consumer<String> c = (t) -> et.withTranslation(() -> myMethod(t)); |
如果没有其他规则可用,则已检查的异常将自动转换为
(免责声明:我是ET的作者)
创建将传播已检查异常的自定义返回类型。这是创建一个新接口的替代方法,该接口通过对功能接口方法的"抛出异常"的轻微修改来镜像现有的功能接口。
定义
CheckedValueSupplier
1 2 3 |
为CheckedValue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | public class CheckedValue<V> { private final V v; private final Optional<Exception> opt; public Value (V v) { this.v = v; } public Value (Exception e) { this.opt = Optional.of(e); } public V get () throws Exception { if (opt.isPresent()) { throw opt.get(); } return v; } public Optional<Exception> getException () { return opt; } public static < T > CheckedValue< T > returns (T t) { return new CheckedValue< T >(t); } public static < T > CheckedValue< T > rethrows (Exception e) { return new CheckedValue< T >(e); } public static <V> CheckedValue<V> from (CheckedValueSupplier<V> sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { return Result.rethrows(e); } } public static <V> CheckedValue<V> escalates (CheckedValueSupplier<V> sup) { try { return CheckedValue.returns(sup.get()); } catch (Exception e) { throw new RuntimeException(e); } } } |
用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | // Don't use this pattern with FileReader, it's meant to be an // example. FileReader is a Closeable resource and as such should // be managed in a try-with-resources block or in another safe // manner that will make sure it is closed properly. // This will not compile as the FileReader constructor throws // an IOException. Function<String, FileReader> sToFr = (fn) -> new FileReader(Paths.get(fn).toFile()); // Alternative, this will compile. Function<String, CheckedValue<FileReader>> sToFr = (fn) -> { return CheckedValue.from ( () -> new FileReader(Paths.get("/home/" + f).toFile())); }; // Single record usage // The call to get() will propagate the checked exception if it exists. FileReader readMe = pToFr.apply("/home/README").get(); // List of records usage List<String> paths = ...; //a list of paths to files Collection<CheckedValue<FileReader>> frs = paths.stream().map(pToFr).collect(Collectors.toList()); // Find out if creation of a file reader failed. boolean anyErrors = frs.stream() .filter(f -> f.getException().isPresent()) .findAny().isPresent(); |
这是怎么回事?
创建抛出已检查异常的单个功能接口(
这种方法存在的问题。
- 我们现在正在抛出"异常",有效地隐藏了最初抛出的特定类型。
-
我们不知道在调用
CheckedValue#get() 之前发生了异常。
消费者等
某些功能接口(例如
功能代替消费者
一种方法是使用函数而不是消费者,这在处理流时适用。
1 2 3 4 5 6 7 8 | List<String> lst = Lists.newArrayList(); // won't compile lst.stream().forEach(e -> throwyMethod(e)); // compiles lst.stream() .map(e -> CheckedValueSupplier.from( () -> {throwyMethod(e); return e;})) .filter(v -> v.getException().isPresent()); //this example may not actually run due to lazy stream behavior |
升级
或者,您始终可以升级到
不要消费。
只需避免功能接口,并使用良好的for循环。
默认情况下,Java 8 Function不允许抛出异常,并且如多个答案中所建议的那样,有很多方法可以实现它,一种方法是:
1 2 3 4 | @FunctionalInterface public interface FunctionWithException<T, R, E extends Exception> { R apply(T t) throws E; } |
定义为:
1 2 3 4 5 6 | private FunctionWithException<String, Integer, IOException> myMethod = (str) -> { if ("abc".equals(str)) { throw new IOException(); } return 1; }; |
并在调用方法中添加
我正在做的是允许用户在异常的情况下给出他实际想要的值。
所以我看起来像这样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public static <T, R> Function<? super T, ? extends R> defaultIfThrows(FunctionThatThrows<? super T, ? extends R> delegate, R defaultValue) { return x -> { try { return delegate.apply(x); } catch (Throwable throwable) { return defaultValue; } }; } @FunctionalInterface public interface FunctionThatThrows<T, R> { R apply(T t) throws Throwable; } |
然后可以这样调用:
1 | defaultIfThrows(child -> child.getID(), null) |
如果您不介意使用第三方库,使用cyclops-react,我参与的库,您可以使用FluentFunctions API来编写
1 |
ofChecked接受一个jOOλCheedFunction并将软化的引用返回给标准(未经检查)的JDK java.util.function.Function。
或者,您可以通过FluentFunctions api继续使用捕获的功能!
例如,要执行您的方法,最多重试5次并记录它的状态,您可以编写
1 2 3 4 | FluentFunctions.ofChecked(this::myMethod) .log(s->log.debug(s),e->log.error(e,e.getMessage()) .try(5,1000) .apply("my param"); |
我是一个带有一些通用魔法的小型lib的作者,可以在任何地方抛出任何Java异常,而无需捕获它们,也不需要将它们包装到
用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | public class UncheckedExceptions { /** * throws {@code exception} as unchecked exception, without wrapping exception. * * @return will never return anything, return type is set to {@code exception} only to be able to write <wyn>throw unchecked(exception)</wyn> * @throws T {@code exception} as unchecked exception */ @SuppressWarnings("unchecked") public static <T extends Throwable> T unchecked(Exception exception) throws T { throw (T) exception; } @FunctionalInterface public interface UncheckedFunction<R> { R call() throws Exception; } /** * Executes given function, * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception. * * @return result of function * @see #unchecked(Exception) */ public static <R> R unchecked(UncheckedFunction<R> function) { try { return function.call(); } catch (Exception e) { throw unchecked(e); } } @FunctionalInterface public interface UncheckedMethod { void call() throws Exception; } /** * Executes given method, * catches and rethrows checked exceptions as unchecked exceptions, without wrapping exception. * * @see #unchecked(Exception) */ public static void unchecked(UncheckedMethod method) { try { method.call(); } catch (Exception e) { throw unchecked(e); } } } |
来源:https://github.com/qoomon/unchecked-exceptions-java
一些提供的解决方案使用E的泛型参数来传递抛出的异常类型。
更进一步,而不是传递异常的类型,传入异常类型的消费者,如...
1 | Consumer<E extends Exception> |
您可以创建几个可重用的
我会做一些通用的事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public interface Lambda { @FunctionalInterface public interface CheckedFunction< T > { T get() throws Exception; } public static < T > T handle(CheckedFunction< T > supplier) { try { return supplier.get(); } catch (Exception exception) { throw new RuntimeException(exception); } } } |
用法:
1 | Lambda.handle(() -> method()); |
使用
此外,它还包含最多16个参数的功能接口。此外,它还提供了在不同场景中使用的Tuple类。
Jool Git Link
特别是在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public void frankTest() { int pageId= -1; List<Book> users= null; try { //Does Not Compile: Object page=DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> new Portal(rw.getInt("id"),"", users.parallelStream().filter(uu -> uu.getVbid() == rw.getString("user_id")).findFirst().get(), rw.getString("name"))); //Compiles: Object page= DatabaseConnection.getSpringConnection().queryForObject("SELECT * FROM bookmark_page", (rw, n) -> { try { final Book bk= users.stream().filter(bp -> { String name= null; try { name = rw.getString("name"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return bp.getTitle().equals(name); }).limit(1).collect(Collectors.toList()).get(0); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return new Portal(rw.getInt("id"),"", users.get(0), rw.getString("name")); } ); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } |