假设我需要找到某个订单的值,然后获取其id,然后获取其localized-id.如果我不能这样做,我想抛出异常:
return values.stream() .filter(value -> value.getOrder("order") == order) .findAny() .map(Attribute::getId) .map(Id::getLocalizedId) .orElseThrow(() -> new RuntimeException("Could not get the localized id of the value of order " + order));
问题是异常不是很详细:它告诉我我不能获得本地化的id,但不是为什么.
我想念一些Optional.ifAbsentThrow方法,它允许我这样做:
return values.stream() .filter(value -> value.getOrder("order") == order) .findAny() .ifAbsentThrow(() -> new RuntimeException("Could not find value of order " + order)); .map(Attribute::getId) .ifAbsentThrow(() -> new RuntimeException("Value of order " + order + " has no id")); .map(Id::getLocalizedId) .orElseThrow(() -> new RuntimeException("Could get the id but not the localized id of the value of order " + order));
为了解决这个问题,我创建了以下ifAbsentThrow方法:
public static <T,X extends RuntimeException> Predicate<T> ifAbsentThrow(Supplier<? extends X> exceptionSupplier) throws RuntimeException { return valor -> { if (valor == null) throw exceptionSupplier.get(); return true; }; }
我这样使用它:
return values.stream() .filter(value -> value.getOrder("order") == order) .findAny() .filter(ifAbsentThrow(() -> new RuntimeException("Could not find value of order " + order)); .map(Attribute::getId) .filter(ifAbsentThrow(() -> new RuntimeException("Value of order " + order + " has no id")); .map(Id::getLocalizedId) .orElseThrow(() -> new RuntimeException("Could get the id but not the localized id of the value of order " + order));
我的问题:
> 1)我在这里遗漏了什么吗?可选是真的错过了这个
功能或我不应该出于某种原因这样做?
> 2)是否有更好的,推荐的方式抛出更详细的例外
对于缺失值?
编辑:现在在我看来,Optional.ifAbsentThrow不存在,因为它将是一种处理空值的方法,而Optional是首先不使用空值.可选显然不能很好地使用空值,如果你混合它会变得冗长.然而,在现实世界中,我发现很难处理这个全有或全无的命题:一些代码被转换为Optionals,而其他代码仍然使用可空值.为了帮助我混合它们,并且只在必要时将nullables重构为Optionals,我相信我将使用下面的GetNonNull类,它基于我从@Alex和@Holgers在本页中获得的知识.
解决方法
可选是为了封装可能缺少的值.如果你执行像ifAbsentThrow这样的操作,那么将值作为Optional是没有意义的,因为你已经知道它在正常完成时并不存在.所以orElseThrow做你想要的但返回一个普通的对象,因为它不再是可选的.
当然,您可以将函数应用于普通对象并将其结果再次包装为Optional,如Alex suggested,但问问自己这是否真的比直接代码有所改进:
Attribute a=values.stream().filter(value -> value.getOrder("order") == order).findAny() .orElseThrow(() -> new RuntimeException("Could not find value of order " + order)); Id id=a.getId(); if(id==null) throw new RuntimeException("Value of order " + order + " has no id"); String name=id.getName(); if(name==null) throw new RuntimeException( "Could get the id but not the localized id of the value of order " + order); return name;
您还可以创建一个实用程序方法,提供应用函数的操作,并在函数返回null时正确抛出:
static <T,R,E extends Throwable> R get(T o,Function<T,R> f,Supplier<E> s) throws E { return Optional.ofNullable(f.apply(o)).orElseThrow(s); }
使用此方法,您的操作变为:
return get(ContainingClass.<Attribute,Id,RuntimeException>get( values.stream().filter(value -> value.getOrder("order") == order).findAny() .orElseThrow( () -> new RuntimeException("Could not find value of order " + order)),Attribute::getId,() -> new RuntimeException("Value of order " + order + " has no id")),Id::getName,() -> new RuntimeException( "Could get the id but not the localized id of the value of order " + order));
(不幸的是,编译器的类型推断达到了极限)
最后一种方法是创建Optional的替代方案,它不仅带有可能缺席的值,还有可选的错误:
public final class Failable<T,E extends Throwable> { private final T value; private final E failure; private Failable(T value,E failure) { this.value=value; this.failure=failure; if(value==null && failure==null) throw new NullPointerException(); } public T get() throws E { if(failure!=null) throw failure; return value; } public <R> Failable<R,E> map(Function<T,Supplier<E> s) { if(value!=null) { R result=f.apply(value); return new Failable<>(result,result!=null? null: s.get()); } // already Failed,types of R and T are irrelevant @SuppressWarnings("unchecked") Failable<R,E> f0=(Failable)this; return f0; } public static <T,E extends Throwable> Failable<T,E> of(Optional<T> o,Supplier<E> s) { return o.map(t -> new Failable<>(t,(E)null)) .orElseGet(()->new Failable<>(null,s.get())); } }
使用此类,您可以将操作编码为
return Failable.of( values.stream().filter(value -> value.getOrder("order") == order).findAny(),() -> new RuntimeException("Could not find value of order " + order)) .map(Attribute::getId,()->new RuntimeException("Value of order "+order+" has no id")) .map(Id::getName,()->new RuntimeException( "Could get the id but not the localized id of the value of order " + order)).get();