我如何从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。
你不能。
然而,你可能想要看看我的一个项目,它可以让你更容易地操纵这种“投掷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()一些默认值。
请注意,这仍然是一项正在进行的工作,更多的是来。更好的名字,更多的功能等等。
您还可以编写一个包装器方法来包装未检查的异常,甚至使用表示另一个功能接口的附加参数(具有相同的返回类型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;
}
你可以用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;
}
也许,更好、更实用的方法是包装异常并在流中进一步传播它们。以Vavr的Try类型为例。
例子:
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)))
OR
@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中几乎所有泛型异常都被视为未检查的。
你不能。
然而,你可能想要看看我的一个项目,它可以让你更容易地操纵这种“投掷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()一些默认值。
请注意,这仍然是一项正在进行的工作,更多的是来。更好的名字,更多的功能等等。