当可选为空时如何return?

How to return when an optional is empty?

我喜欢 optionals 现在在 Java 标准库中。但是我一直 运行 有一个基本问题,我还没有想出如何以最好的方式解决(最容易阅读和理解,最漂亮,最短):

如何在可选为空时从方法中return?

我正在寻找适用于可选数量和代码块大小的不同组合的通用解决方案。

在下面的示例中,我将尝试说明我的意思:

void m1() {
    // When I get an optional:
    Optional<String> o = getOptional();

    // And want to return if it's empty
    if (!o.isPresent()) return;
    
    // In the whole rest of the method I have to call Optional.get 
    // every time I want the value:
    System.out.println(o.get());
    
    // Which is pretty ugly and verbose!
}


void m2() {
    // If I instead return null if a value is absent:
    String s = getNullabe();
    if (s == null) return;
    
    // Then I can use the value directly:
    System.out.println(s);
}

这个问题是关于如何获得上述两个示例的优点:可选类型的安全类型和可空类型的简洁性。

其余示例进一步说明了这一点。

void m3() {
    // If I on the other hand want to throw on empty that's pretty and compact:
    String s = getOptional()
        .orElseThrow(IllegalStateException::new);
    
    System.out.println(s);
}

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;
    
    // I can of course declare a new variable for the un-optionalised string:
    String s = o.get();

    System.out.println(s);
    
    // But the old variable still remains in scope for the whole method 
    // which is ugly and annoying.
    System.out.println(o.get());
}


void m5() {
    // This is compact and maybe pretty in some ways:
    getOptional().ifPresent(s -> {
        System.out.println(s);

        // But the extra level of nesting is annoying and it feels 
        // wrong to write all the code in a big lambda.
        
        getOtherOptional().ifPresent(i -> {
            // Also, more optional values makes it really weird and 
            // pretty hard to read,  while with nullables I would 
            // get no extra nesting, it would looks good and be 
            // easy to read.
            System.out.println("i: " + i);
            
            // It doesn't work in all cases either way.
        });
    });
}


Optional<String> getOptional() {
    throw new UnsupportedOperationException();
}

Optional<Integer> getOtherOptional() {
    throw new UnsupportedOperationException();
}

String getNullabe() {
    throw new UnsupportedOperationException();
}

如果可选项为空,我如何从方法中 return,而不必在方法的其余部分使用 get,无需声明额外的变量,也无需额外的块嵌套级别?

或者如果不可能得到所有这些,处理这种情况的最佳方法是什么?

你可以使用 orElse(null):

String o = getOptional().orElse(null);
if (o == null) {
    return;
}

您正在使用的 ifPresent 不需要您创建一个新的 lambda,您可以只使用一个方法参考:

getOptional().ifPresent(System.out::println);

不过,这并不能真正解决您想以两个可选值的存在为条件的情况。但作为

的替代品
// And want to return if it's empty
if (!o.isPresent()) return;

为什么不直接反转条件,这在嵌套情况下也能很好地工作?没有必要明确 return:

if (o.isPresent()) {
  System.out.println(o.get());
  if (oo.isPresent()) {
    System.out.println(oo.get());
  }
}

但是,这种用例表明与可为 null 的值相比,您并没有真正从 Optional 中受益。一般来说,如果您使用 isPresent 和 get,那么 Optional 可能不会真正为您提供那么多(除非它迫使您考虑缺少值的情况)。使用 ifPresent、map、filter 和其他 "more functional" 方法可能是可选值的更典型用途。


但是在任何情况下,请不要 return null 当您承诺一个 Optional 时。虽然在期望对象时 return null 是完全合法的,但 Optional 的要点恰恰是为了避免必须检查 null。所以不要这样做:

Optional<String> getOptional() {
    return null;
}

而是做:

Optional<String> getOptional() { 
  return Optional.empty();
}

否则你最终不得不做:

Optional<String> o = getOptional();
if (o != null && o.isPresent()) {
  // ...
}

其实就是把同样的事情做了两次。使用 Optional,或使用可为 null 的值,但不要两者都做!

我不认为你所问的实际上是可能的,但我想建议只获取所有直接在你的 String 上运行的代码并将其包装在一个函数中。所以你的函数变成这样:

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    doThings(o.get());
}

void doThings(String s){
    System.out.println(s);
    //do whatever else with the string.
}

这样您只有字符串在范围内,您不必每次都调用 .get() 来访问它。

你可以使用 ifPresentmap 方法,如果函数无效并且你需要做副作用你可以使用 ifPresent,

optional.ifPresent(System.out::println); 

如果另一个方法 return 依赖于 Optional,那么该方法可能还需要 return Optional 并使用 map 方法

Optional<Integer> getLength(){
    Optional<String> hi = Optional.of("hi");
    return hi.map(String::length)
}

大多数 的时间当你调用 isPresentget 时,你在误用 Optional.

这是个好话题,我们都喜欢函数式编程风格!

通常在开始执行方法时,您会在顶部获得可选权限。 在这一点上你开始想知道,你能做些什么来处理一个空的可选, 只有在这种情况下退出并停止处理才有意义。

第 1 步 - 探索和分析

public void processMedia(String mediaClassName, String mediaName) {

    // THAT MIGHT BE YOUR FIRST IDEA
    MediaClass mediaClass = mediaClassFinder.find(mediaClassName).orElse(null); 

    // RETURNING ON NULL CONDITION LIKE THE BELOW CAN BE ALRIGHT,
    // BUT POSSIBLY YOU CAN DO BETTER
    if (mediaClass == null) {
        return;
    }
    Optional<Media> media = mediaFinder.find(mediaClass.getId(), mediaName);

    // do processing

    // render the processed object
}

第 2 步 最好的方法可能是将实现的各个部分提取到单独的方法中,然后以函数式的方式将它们链接在一起。 作为此练习的副作用,您可能最终会得到大大改进的应用程序界面和结构。 这就是重构的工作原理。看下面,没有显式的空赋值,也没有任何额外的 return 点。编码变得有趣。

public void processMedia(String mediaClassName, String mediaName) {
    mediaClassFinder.find(mediaClassName)
        .flatMap(mediaClass -> mediaFinder.find(mediaClass.getId(), mediaName))
        .map(this::compress)
        .ifPresent(this::render);
}
private Media compress(Media media) {
    // compress media implementation
    return media;
}
private void render(Media media) {
    // render media implementation
}

我希望你喜欢我的例子:)