在lambda中处理检查异常是个比较大的问题,有的时候我们会将它转换成一个运行时异常再向上抛出
myList.stream()
.map(item -> {
try {
return doSomething(item);
} catch (MyException e) {
throw new RuntimeException(e);
}
})
.forEach(System.out::println);
或者这样:
myList.stream()
.map(this::trySomething)
.forEach(System.out::println);
private Item trySomething(Item item) {
try {
return doSomething(item);
} catch (MyException e) {
throw new RuntimeException(e);
}
}
上面这种方式我们每次都要有try catch,下面来看一种更简单的方式:
STEP1:
@FunctionalInterface
public interface CheckedFunction<T,R> {
R apply(T t) throws Exception;
}
STEP2:
public static <T,R> Function<T,R> wrap(CheckedFunction<T,R> checkedFunction) {
return t -> {
try {
return checkedFunction.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
然后就可以愉快的使用了:
myList.stream()
.map(wrap(item -> doSomething(item)))
.forEach(System.out::println);
这种方式有个问题,就是出现异常,整个lambda就GG了,那么有没有其他的方式呢?
这里介绍一个Either类,左面存异常,右面存返回结果:
STEP1:
public class Either<L, R> {
private final L left;
private final R right;
private Either(L left, R right) {
this.left = left;
this.right = right;
}
public static <L,R> Either<L,R> Left( L value) {
return new Either(value, null);
}
public static <L,R> Either<L,R> Right( R value) {
return new Either(null, value);
}
public Optional<L> getLeft() {
return Optional.ofNullable(left);
}
public Optional<R> getRight() {
return Optional.ofNullable(right);
}
public boolean isLeft() {
return left != null;
}
public boolean isRight() {
return right != null;
}
public <T> Optional<T> mapLeft(Function<? super L, T> mapper) {
if (isLeft()) {
return Optional.of(mapper.apply(left));
}
return Optional.empty();
}
public <T> Optional<T> mapRight(Function<? super R, T> mapper) {
if (isRight()) {
return Optional.of(mapper.apply(right));
}
return Optional.empty();
}
public String toString() {
if (isLeft()) {
return "Left(" + left +")";
}
return "Right(" + right +")";
}
}
STER2:
public static <T,R> Function<T, Either> lift(CheckedFunction<T,R> function) {
return t -> {
try {
return Either.Right(function.apply(t));
} catch (Exception ex) {
return Either.Left(ex);
}
};
}
然后就可以使用了:
myList.stream()
.map(Either.lift(item -> doSomething(item)))
.forEach(System.out::println);
然后我们就可以用Filter过滤出来了,或者我们只过滤右边把异常干掉
然鹅,现在又有问题了,我们有的时候收集造成异常的数据,我们就可以常用这种方式:
public static <T,R> Function<T, Either> liftWithValue(CheckedFunction<T,R> function) {
return t -> {
try {
return Either.Right(function.apply(t));
} catch (Exception ex) {
return Either.Left(Pair.of(ex,t));
}
};
}
左面的是:
public class Pair<F,S> {
public final F fst;
public final S snd;
private Pair(F fst, S snd) {
this.fst = fst;
this.snd = snd;
}
public static <F,S> Pair<F,S> of(F fst, S snd) {
return new Pair<>(fst,snd);
}
}
这样,我们就可以获得造成异常的原始值然后愉快的进行重试了,这种在大数据上感觉比较好用