首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >区分Optional.empty()和Optional.ofNullable(null)

区分Optional.empty()和Optional.ofNullable(null)
EN

Stack Overflow用户
提问于 2022-10-18 04:06:38
回答 2查看 74关注 0票数 0

用例是在一个可选的包中封装一些返回值,以通信两个场景:

  1. 返回值不是有意出现的。
  2. 返回值应该是存在的,但意外地为空。

例如:

代码语言:javascript
复制
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。这在这个用例中是有问题的,因为没有这样的元素被重载,这意味着两者都是:

  1. 选项为空。
  2. 可选项的值为空。

有办法区分这些情景吗?我理解这正是选项的意图,以防止执行get()和获得空值,但我想知道是否还有其他模式来实现这一点。

EN

回答 2

Stack Overflow用户

发布于 2022-10-18 04:25:28

下面是一个示例,说明如何将该模式快速封装到实用程序类中:

代码语言:javascript
复制
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中的样板。
票数 1
EN

Stack Overflow用户

发布于 2022-10-18 08:21:07

因此,简单地说,在业务逻辑中有两种情况,您需要区分它们,这在当前的实现中是不可能的。

没关系,这是可以理解的。

但是,你期望找到解决方案的方向似乎是错误的。

首先要记住的是,null和一个空的可选选项都是为了传递相同的东西-- no data (因此,不引用对象)。当其中之一在您的应用程序中具有特殊意义时,它是一种气味。这不是该走的路。发明一个新的容器也是一回事。

要正确地解决问题,关键是要理解当前的问题陈述是不正确的。

可能的解决办法

首先,你需要仔细区分这两种情况。他们是否都被视为正常

我怀疑在第一种情况下,当您故意不想返回时,数据可能是异常的(例如,错误的凭据、无效的提取金额等)。log.error()记录这个事件的方式意味着,这并不意味着您的应用程序将如何工作。

如果这个假设是正确的,那么您可以抛出一个异常,而不是返回。

代码语言:javascript
复制
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的“空实例”(可以存储在静态的最终字段中),以表示预期数据将被返回,但没有找到适当的信息。

代码语言:javascript
复制
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
}

在这种情况下,第二种方法的逻辑会是怎样的:

代码语言:javascript
复制
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()检查。
  • 我建议第一种方法而不是第二种方法。因为它更明确、更容易掌握和实现,而且也符合您记录某些严重事件的意图。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/74105594

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档