假设我需要找到某个order的order,然后得到它的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));我的问题:
编辑:现在在我看来,Optional.ifAbsentThrow并不存在,因为它将是处理null值的一种方式,而Optional首先就是不使用null值。Optional显然不能很好地处理空值,如果将它们混合在一起,就会变得冗长。然而,在现实世界中,我发现很难处理这个要么全是零的命题:一些代码被翻译成选项,而另一些则使用可空值。为了帮助我把它们混在一起,并且只在必要的时候将零符号重构成选项,我相信我将使用下面的GetNonNull类,建立在我从这个页面中的@Alex和@Holgers的答案中获得的知识的基础上。
发布于 2015-02-03 00:52:18
基于本页中从@Alex和@Holgers答案中获得的知识,我开发了以下GetNonNull类,重点是可读性:
Optional<Value> value = values.stream()
.filter(value -> value.getOrder("order") == order)
.findAny();
return GetNonNull
.mapOrThrow(value, () -> new RuntimeException("Got no value."))
.mapOrThrow(Value::getAttribute, () -> new RuntimeException("Got value, but no attribute."))
.mapOrThrow(Attribute::getId, () -> new RuntimeException("Got value and attribute, but no id."))
.mapOrThrow(Id::getLocalizedId, () -> new RuntimeException("Got value, attribute and id, but no localized id."))
.get();我发现这个函数代码非常容易阅读,它将所有异常处理代码集中在一起(不需要将orElseThrow添加到流的末尾)。GetNonNull名称意味着最终结果永远不会为null。
将其与替代命令式代码进行比较:
if (valor == null) throw new RuntimeException("Got no value.");
Attribute attribute = valor.getAttribute();
if (attribute == null) throw new RuntimeException("Got value, but no attribute.");
Id id = attribute.getId();
if (id == null) throw new RuntimeException("Got value and attribute, but no id.");
String localizedId = id.getLocalizedId();
if (localizedId == null) throw new RuntimeException("Got value, attribute and id, but no localized id.");
return localizedId;您也可以返回一个Optional,而不是抛出
return GetNonNull
.mapOrThrow(value, () -> new RuntimeException("Got no value."))
.mapOrThrow(Value::getAttribute, () -> new RuntimeException("Got value, but no attribute."))
.mapOrThrow(Attribute::getId, () -> new RuntimeException("Got value and attribute, but no id."))
.getOptional(Id::getLocalizedId); // Changed here.或者您可以返回一个非null默认值:
return GetNonNull
.mapOrThrow(value, () -> new RuntimeException("Got no value."))
.mapOrThrow(Value::getAttribute, () -> new RuntimeException("Got value, but no attribute."))
.mapOrThrow(Attribute::getId, () -> new RuntimeException("Got value and attribute, but no id."))
.getOrDefault(Id::getLocalizedId, "DEFAULT"); // Changed here.它也是Optional/null不可知论的,也就是说,如果初始值是常规的可空值,而不是Optional,则不会发生任何变化。
Value value = ...; // Not an Optional.
return GetNonNull
.mapOrThrow(value, () -> new RuntimeException("Got no value."))
.mapOrThrow(Value::getAttribute, () -> new RuntimeException("Got value, but no attribute."))
.mapOrThrow(Attribute::getId, () -> new RuntimeException("Got value and attribute, but no id."))
.mapOrThrow(Id::getLocalizedId, () -> new RuntimeException("Got value, attribute and id, but no localized id."))
.get();您也可以使用它作为一个简单的函数获取默认值成语。这是:
Value value = ...;
if (value != null) return value;
else if (default != null) return default;
else throw new NullPointerExeption();可以写成:
// Shorter and more readable then Optional.ofNullable(value).orElse(default).
// Also not the same, because here a NullPointerException is raised if default is null.
return GetNonNull.getOrDefault(value, default);这是:
Optional<Value> value = ...;
if (value.isPresent()) return value;
else if (default != null) return default;
else throw new NullPointerExeption();也可以写成完全相同的文字:
return GetNonNull.getOrDefault(value, default);由于GetNonNull类与not和Optionals兼容,如果一些使用可空值的遗留命令式代码稍后被重构为使用选项,则不需要更改GetNonNull用法。
下面是:
public final class GetNonNull<T, E extends Throwable> {
private final T value;
private final E failure;
private GetNonNull(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> Optional<R> getOptional(Function<T, R> f) throws E {
if (failure != null) throw failure;
if (value != null) {
R result = f.apply(value);
return Optional.ofNullable(result);
}
return Optional.empty();
}
public static <R> R getOrDefault(R o1, Supplier<R> supplier) {
if (o1 != null) return o1;
R result = supplier.get();
if (result != null) return result;
else throw new NullPointerException();
}
public static <R> R getOrDefault(R o1, R o2) {
if (o1 != null) return o1;
else if (o2 != null) return o2;
else throw new NullPointerException();
}
public static <R> R getOrDefault(Optional<R> o1, R o2) {
if (o1.isPresent()) return o1.get();
else if (o2 != null) return o2;
else throw new NullPointerException();
}
public <R> R getOrDefault(Function<T, R> f, R o) throws E {
if (failure != null) throw failure;
if (value != null) {
R result = f.apply(value);
if (result != null) return result;
else return o;
}
return o;
}
public <R> GetNonNull<R, E> mapOrThrow(Function<T, R> f, Supplier<E> s) {
if (value != null) {
R result = f.apply(value);
return new GetNonNull<>(result, (result != null) ? null : s.get());
}
return (GetNonNull)this;
}
public static <T, E extends Throwable> GetNonNull<T, E> getOrThrow(Optional<T> o, Supplier<E> s) {
return o.map(t -> new GetNonNull<>(t, (E)null)).orElseGet(() -> new GetNonNull<>(null, s.get()));
}
public static <T, E extends Throwable> GetNonNull<T, E> mapOrThrow(T o, Supplier<E> s) {
return getOrThrow(Optional.ofNullable(o), s);
}
}发布于 2015-02-01 23:10:43
Optional没有一个ifAbsentThrow方法,如果它存在,它会返回Optional。最近的是orElseThrow,它从可选的返回值。它不能工作,因为这是Optional#filter的实现
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}如您所见,如果Predicate不存在,它就不会使用它,所以您的filter什么也不做。
您可以这样做的一种方法是使用orElseThrow,然后在使用ofNullable应用映射函数之后重新包装结果。
Optional<Attribute> o = //get your first optional from the stream.
return Optional.ofNullable(Optional.ofNullable(
o.orElseThrow(() -> new RuntimeException("Could not find value of order " + order))
.getId())
.orElseThrow(() -> new RuntimeException("Value of order " + order + " has no id"))
.getName())
.orElseThrow(() -> new RuntimeException("Could get the id but not the localized id of the value of order " + order));如果您认为这样的语句更易读,也可以将其分解为单独的语句。
另一种方法是将Attribute#getId和Id#getName更改为返回Optionals而不是null。然后看起来是这样的:
return values.stream()
.filter(value -> value.getOrder("order") == order)
.findAny()
.orElseThrow(() -> new RuntimeException("Could not find value of order " + order))
.getId()
.orElseThrow(() -> new RuntimeException("Value of order " + order + " has no id"))
.getName()
.orElseThrow(() -> new RuntimeException("Could get the id but not the localized id of the value of order " + order));我更喜欢这种方式,因为您不需要用ofNullable重新包装返回值,并且它让调用这些方法的其他人知道返回值是可选的,但是如果您不能更改它们,那么第一种方法就能正常工作。
发布于 2015-02-04 00:55:26
假设的ifAbsentThrow(...)方法可以用现有的Optional方法来表示,如下所示:
.map(Optional::of).orElseThrow(...)然后,您的原始示例如下:
return values.stream()
.filter(value -> value.getOrder("order") == order)
.findAny()
.map(Optional::of)
.orElseThrow(() -> new RuntimeException("Could not find value of order " + order))
.map(Attribute::getId)
.map(Optional::of)
.orElseThrow(() -> 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));https://stackoverflow.com/questions/28268751
复制相似问题