引发异常的 Java 8 Lambda 函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18198176/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java 8 Lambda function that throws exception?
提问by Triton Man
I know how to create a reference to a method that has a String
parameter and returns an int
, it's:
我知道如何创建对具有String
参数并返回 的方法的引用int
,它是:
Function<String, Integer>
However, this doesn't work if the function throws an exception, say it's defined as:
但是,如果函数抛出异常,这将不起作用,比如它被定义为:
Integer myMethod(String s) throws IOException
How would I define this reference?
我将如何定义这个参考?
采纳答案by jason
You'll need to do one of the following.
您需要执行以下操作之一。
If it's your code, then define your own functional interface that declares the checked exception:
@FunctionalInterface public interface CheckedFunction<T, R> { R apply(T t) throws IOException; }
and use it:
void foo (CheckedFunction f) { ... }
Otherwise, wrap
Integer myMethod(String s)
in a method that doesn't declare a checked exception:public Integer myWrappedMethod(String s) { try { return myMethod(s); } catch(IOException e) { throw new UncheckedIOException(e); } }
and then:
Function<String, Integer> f = (String t) -> myWrappedMethod(t);
or:
Function<String, Integer> f = (String t) -> { try { return myMethod(t); } catch(IOException e) { throw new UncheckedIOException(e); } };
如果是您的代码,则定义您自己的功能接口来声明已检查的异常:
@FunctionalInterface public interface CheckedFunction<T, R> { R apply(T t) throws IOException; }
并使用它:
void foo (CheckedFunction f) { ... }
否则,包装
Integer myMethod(String s)
在一个不声明检查异常的方法中:public Integer myWrappedMethod(String s) { try { return myMethod(s); } catch(IOException e) { throw new UncheckedIOException(e); } }
进而:
Function<String, Integer> f = (String t) -> myWrappedMethod(t);
或者:
Function<String, Integer> f = (String t) -> { try { return myMethod(t); } catch(IOException e) { throw new UncheckedIOException(e); } };
回答by Adam R. Nelson
Disclaimer: I haven't used Java 8 yet, only read about it.
免责声明:我还没有使用过 Java 8,只是阅读了它。
Function<String, Integer>
doesn't throw IOException
, so you can't put any code in it that throws IOException
. If you're calling a method that expects a Function<String, Integer>
, then the lambda that you pass to that method can't throw IOException
, period. You can either write a lambda like this (I think this is the lambda syntax, not sure):
Function<String, Integer>
不会抛出IOException
,所以你不能在里面放任何代码throws IOException
。如果您正在调用一个需要 a 的方法Function<String, Integer>
,那么您传递给该方法的 lambda 不能抛出IOException
,句号。您可以编写这样的 lambda(我认为这是 lambda 语法,不确定):
(String s) -> {
try {
return myMethod(s);
} catch (IOException ex) {
throw new RuntimeException(ex);
// (Or do something else with it...)
}
}
Or, if the method you're passing the lambda to is one you wrote yourself, you can define a new functional interface and use that as the parameter type instead of Function<String, Integer>
:
或者,如果您将 lambda 传递给的方法是您自己编写的方法,您可以定义一个新的函数式接口并将其用作参数类型而不是Function<String, Integer>
:
public interface FunctionThatThrowsIOException<I, O> {
O apply(I input) throws IOException;
}
回答by assylias
This is not specific to Java 8. You are trying to compile something equivalent to:
这不是 Java 8 特有的。您正在尝试编译与以下内容等效的内容:
interface I {
void m();
}
class C implements I {
public void m() throws Exception {} //can't compile
}
回答by yohan
Another solution using a Function wrapper would be to return either an instance of a wrapper of your result, say Success, if everything went well, either an instance of, say Failure.
使用函数包装器的另一种解决方案是返回结果的包装器实例,例如成功,如果一切顺利,则是失败的实例。
Some code to clarify things :
一些代码来澄清事情:
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<B>> tryOf(ThrowableFunction<A, B> function) {
return a -> {
try {
B result = function.apply(a);
return new Success<B>(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;
}
}
A simple use case :
一个简单的用例:
List<Try<Integer>> result = Lists.newArrayList(1, 2, 3).stream().
map(Try.<Integer, Integer>tryOf(i -> someMethodThrowingAnException(i))).
collect(Collectors.toList());
回答by mmounirou
What I'm doing is to allow the user to give the value he actually want in case of exception . So I've something looking like this
我正在做的是允许用户在异常情况下给出他实际想要的值。所以我有这样的东西
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;
}
And this can then be call like :
然后可以这样调用:
defaultIfThrows(child -> child.getID(), null)
回答by jlb
You can actually extend Consumer
(and Function
etc.) with a new interface that handles exceptions -- using Java 8's default methods!
您实际上可以使用处理异常的新接口扩展Consumer
(Function
等等)——使用 Java 8 的默认方法!
Consider this interface (extends Consumer
):
考虑这个接口(扩展Consumer
):
@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;
}
Then, for example, if you have a list:
然后,例如,如果您有一个列表:
final List<String> list = Arrays.asList("A", "B", "C");
If you want to consume it (eg. with forEach
) with some code that throws exceptions, you would traditionally have set up a try/catch block:
如果你想forEach
用一些抛出异常的代码来使用它(例如 with ),你传统上会设置一个 try/catch 块:
final Consumer<String> consumer = aps -> {
try {
// maybe some other code here...
throw new Exception("asdas");
} catch (final Exception ex) {
System.out.println("handling an exception...");
}
};
list.forEach(consumer);
But with this new interface, you can instantiate it with a lambda expression and the compiler will not complain:
但是使用这个新接口,您可以使用 lambda 表达式实例化它,编译器不会抱怨:
final ThrowingConsumer<String> throwingConsumer = aps -> {
// maybe some other code here...
throw new Exception("asdas");
};
list.forEach(throwingConsumer);
Or even just cast it to be more succinct!:
或者甚至只是将其转换为更简洁!:
list.forEach((ThrowingConsumer<String>) aps -> {
// maybe some other code here...
throw new Exception("asda");
});
Update: Looks like there's a very nice utility library part of Duriancalled Errorswhich can be used to solve this problem with a lot more flexibility. For example, in my implementation above I've explicitly defined the error handling policy (System.out...
or throw RuntimeException
), whereas Durian's Errors allow you to apply a policy on the fly via a large suite of utility methods. Thanks for sharing it, @NedTwigg!.
更新:看起来Durian有一个非常好的实用程序库部分,称为Errors,可用于以更大的灵活性解决此问题。例如,在我上面的实现中,我明确定义了错误处理策略 (System.out...
或throw RuntimeException
),而 Durian 的错误允许您通过大量实用方法即时应用策略。感谢分享,@NedTwigg!。
Sample usage:
示例用法:
list.forEach(Errors.rethrow().wrap(c -> somethingThatThrows(c)));
回答by fge
This problem has been bothering me as well; this is why I have created this project.
这个问题也一直困扰着我;这就是我创建这个项目的原因。
With it you can do:
有了它,您可以:
final ThrowingFunction<String, Integer> f = yourMethodReferenceHere;
There are a totla of 39 interfaces defined by the JDK which have such a Throwing
equivalent; those are all @FunctionalInterface
s used in streams (the base Stream
but also IntStream
, LongStream
and DoubleStream
).
JDK 总共定义了 39 个接口,它们具有这样的Throwing
等效项;这些都是@FunctionalInterface
在流中使用的 s(基础Stream
还有IntStream
,LongStream
和DoubleStream
)。
And as each of them extend their non throwing counterpart, you can directly use them in lambdas as well:
由于它们每个都扩展了它们的非抛出对应物,因此您也可以直接在 lambdas 中使用它们:
myStringStream.map(f) // <-- works
The default behavior is that when your throwing lambda throws a checked exception, a ThrownByLambdaException
is thrown with the checked exception as the cause. You can therefore capture that and get the cause.
默认行为是,当您抛出的 lambda 抛出已检查的异常时,ThrownByLambdaException
会以已检查的异常作为原因抛出 a 。因此,您可以捕获它并找出原因。
Other features are available as well.
其他功能也可用。
回答by Franky Knuckels
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();
}
}
回答by Sergio
I had this problem with Class.forName and Class.newInstance inside a lambda, so I just did:
我在 lambda 中遇到了 Class.forName 和 Class.newInstance 的问题,所以我只是做了:
public Object uncheckedNewInstanceForName (String name) {
try {
return Class.forName(name).newInstance();
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
Inside the lambda, instead of calling Class.forName("myClass").newInstance() I just called uncheckedNewInstanceForName ("myClass")
在 lambda 中,我没有调用 Class.forName("myClass").newInstance() 我只是调用了 uncheckedNewInstanceForName ("myClass")
回答by Ned Twigg
I think Durian's Errors
classcombines many of the pros of the various suggestions above.
我认为Durian 的Errors
课程结合了上述各种建议的许多优点。
- Wrap a throwing function to a standard Java 8 functional interface.
- Easily specify various policies for handling errors
- When wrapping a method that returns a value, there is an important distinctionbetween specifying a default value or rethrowing a RuntimeException.
- Throwing versionsof Java 8's functional interfaces
- Similar to fge's answer
- Standard interfaces for throwing specific exceptions
- Which addresses Zoltán's concern
- 将抛出函数包装到标准 Java 8 函数式接口中。
- 轻松指定处理错误的各种策略
- 包装返回值的方法时,指定默认值或重新抛出 RuntimeException 之间存在重要区别。
- Java 8 函数式接口的
抛出版本
- 类似于fge 的回答
- 用于抛出特定异常的标准接口
- 这解决了Zoltán 的担忧
To include Durian in your project, you can either:
要将 Durian 包含在您的项目中,您可以:
- grab it from jcenteror maven centralat
com.diffplug.durian:durian:3.3.0
- or just copy paste just two small classes into your code:
Throwing.java
andErrors.java
- 从抓住它jcenter或Maven的中央处
com.diffplug.durian:durian:3.3.0
- 或者只是将两个小类复制粘贴到您的代码中:
Throwing.java
和Errors.java