用例是在一个可选的包中封装一些返回值,以通信两个场景:
例如:
Optional<Foo> myMethodToGetAFoo() {
boolean shouldWeGetAFoo = [business logic];
if (!shouldWeGetAFoo) {
return Optional.empty(); // communicate that we intentionally don't return a foo
}
Foo myFooOrNull = serviceCallToGetAFoo();
Optional<Foo> returnValue = Optional.ofNullable(myFooOrNull); // communicate that we do return a foo, which may be null
}
void myMethodThatUsesAFoo() {
Optional<Foo> myFoo = myMethodToGetAFoo();
// what I would like to do
if (myFoo.isPresent()) {
if (myFoo.get() == null) { // ******* however, this throws NoSuchElementException if it is of null
// should have been present, but service call returned null (bad)
log.error("bad"); return;
}
myFoo.get().useAsNormal();
} else {
// intentionally not present (fine)
log.info("all good"); return;
}
}但是,这是不可能的,因为对null值的可选项调用get()会引发NoSuchElementException。这在这个用例中是有问题的,因为没有这样的元素被重载,这意味着两者都是:
有办法区分这些情景吗?我理解这正是选项的意图,以防止执行get()和获得空值,但我想知道是否还有其他模式来实现这一点。
发布于 2022-10-18 04:25:28
下面是一个示例,说明如何将该模式快速封装到实用程序类中:
import java.util.function.Function;
import java.util.function.Supplier;
public class Result<T> {
private final boolean required;
private final T value;
private Result(boolean required, T value) {
this.required = required;
this.value = value;
}
public static <T> Result<T> notRequired() {
return new Result<>(false, null);
}
public static <T> Result<T> ofNullable(T value) {
return new Result<>(true, value);
}
public <U> U consume(Supplier<U> notRequired, Supplier<U> unavailable, Function<T,U> available) {
if (!required) {
return notRequired.get();
} else {
return value == null ? unavailable.get() : available.apply(value);
}
}
public static void main(String[] args) {
Result<Integer> r = Result.ofNullable(10);
System.out.println(r.consume(() -> "all good", () -> "bad", x -> "$" + x));
}
}在此实现中,您可以更改许多细节:
consume。myMethodToGetAFoo中的样板。发布于 2022-10-18 08:21:07
因此,简单地说,在业务逻辑中有和两种情况,您需要区分它们,这在当前的实现中是不可能的。
没关系,这是可以理解的。
但是,你期望找到解决方案的方向似乎是错误的。
首先要记住的是,null和一个空的可选选项都是为了传递相同的东西-- no data (因此,不引用对象)。当其中之一在您的应用程序中具有特殊意义时,它是一种气味。这不是该走的路。发明一个新的容器也是一回事。
要正确地解决问题,关键是要理解当前的问题陈述是不正确的。
可能的解决办法
首先,你需要仔细区分这两种情况。他们是否都被视为正常?
我怀疑在第一种情况下,当您故意不想返回时,数据可能是异常的(例如,错误的凭据、无效的提取金额等)。log.error()记录这个事件的方式意味着,这并不意味着您的应用程序将如何工作。
如果这个假设是正确的,那么您可以抛出一个异常,而不是返回。
Optional<Foo> myMethodToGetAFoo() {
boolean shouldWeGetAFoo = [business logic];
if (!shouldWeGetAFoo) {
throw new MyException("You shall not pass!"); // use the appropriate exception type and message here
}
Foo myFooOrNull = serviceCallToGetAFoo();
return Optional.ofNullable(myFooOrNull); // optional either empty or contains a result
}如果您正在使用一个框架,比如Spring,您可以利用它的内置异常处理机制来正确地处理这个异常。
如果您不想通过log.error()抛出异常,这将与您认为这种行为不正常的意图相矛盾,另一种选择可能是让Foo实现空对象模式。
可以提供Foo的“空实例”(可以存储在静态的最终字段中),以表示预期数据将被返回,但没有找到适当的信息。
Optional<Foo> myMethodToGetAFoo() {
boolean shouldWeGetAFoo = [business logic];
if (!shouldWeGetAFoo) {
return Optional.empty(); // communicate that we intentionally don't return a foo
}
Foo myFooOrNull = serviceCallToGetAFoo();
return Optional.ofNullable(null).or(() -> Optional.of(Foo.NULL_INSTANCE)); // communicate that we do return a foo, which may be null
}在这种情况下,第二种方法的逻辑会是怎样的:
void myMethodThatUsesAFoo() {
Optional<Foo> myFoo = myMethodToGetAFoo();
myFoo.ifPresentOrElse(foo -> {
if (foo == Foo.NULL_INSTANCE) log.error("bad");
foo.useAsNormal(); // behavior of NULL_INSTANCE.useAsNormal() should be neutral
},
() -> { log.info("all good"); }
);
}注:
Optional.isPresent()时-暂停一秒钟,很可能您并没有充分利用可选的API。当您习惯了它时,很少需要使用isPresent()检查。https://stackoverflow.com/questions/74105594
复制相似问题