有没有更清晰的方法来处理短暂的可选项目?

时间:2019-07-31 00:20:11

标签: java optional

我喜欢Java中的Optional。在一个简单的类中,它使我可以清楚地识别出可能可用或不可用的返回类型和参数。

我遇到的一件事是必须将其分配给一个短期变量,然后将其继承到每个后续范围中。

在使用像这样的可选变量时,我喜欢使用简单变量名opt

   Optional<ThingA> opt = maybeGetThing();

   if (opt.isPresent()) {
      ThingA usefulVariableName = opt.get();
      ...

但是当我需要在此范围内使用变量名时...

void method() {
   Optional<ThingA> opt = maybeGetThing();

   if (opt.isPresent()) {
      ThingA usefulVariableName = opt.get();

      usefulVariableName.doA();
      usefulVariableName.doB();
      usefulVariableName.doC();

      // Duplicate local variable opt
      Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
   }
}

我可以使用optAoptB之类的东西。但我想知道是否还有另一种无需枚举我的临时变量即可编写此代码的方法。这只是一些诸如a aaaa aaaaaabbb之类的惰性变量名称。

我不想像这样明确地命名所有可选项:

   Optional<ThingA> optUsefulVariableName = maybeGetThing();

   if (optUsefulVariableName.isPresent()) {
      ThingA usefulVariableName = optUsefulVariableName.get();
      ...

虽然准确,但非常冗长。我还尝试使用诸如opti之类的一次性名称来表明它们实际上只是临时的,不应超出其直接范围(即使它们将被继承)也没有任何作用。


更新:

我已经看到了使用ifPresent()的建议,但我看不到如何在可选值为空的情况下也需要执行操作的实例中使用它:

void method() {
   Optional<ThingA> opt = maybeGetThing();

   if (!opt.isPresent()) {
      doSomethingOnlyHere();

      return;
   }

   if (opt.isPresent()) {
      ThingA usefulVariableName = opt.get();

      usefulVariableName.doA();
      usefulVariableName.doB();
      usefulVariableName.doC();

      // Duplicate local variable opt
      Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
   }
}

当我尝试使用ifPresent()重构时:

void method() {
   // Doesn't handle instance where I need side effects on an empty optional
   maybeGetThing().ifPresent(usefulVariableName -> {
      ...
   }
}

2 个答案:

答案 0 :(得分:1)

消除变量和调用Optional#get的最基本方法是使用Optional.ifPresent,如果Optional有一个值,它将调用一个函数。

maybeGetThing().ifPresent(val -> {
    // do stuff with side effects here
});

这仍然是使用Optional的一种有限方式,因为Optionals的主要目的之一是促进功能性风格的编程。如果您是初学者,这可能会让您有些失落,但是您的想法是拥有返回某些内容的函数,而不是依赖于side effects的函数。依赖于副作用的功能不能被链接在一起,并且通常很难推理。

从技术上讲,Optional是一种称为函子的函子(来自类别理论)。它是一个围绕值的包装器(无论T是什么),它允许值通过一系列操作传递给它进行操作,然后将其传递给下一个操作,直到我们有了所需的东西,然后是链的操作以终端(即最终)操作结束。终端操作可能会返回未包装的值(如果存在),或者会抛出或返回一些默认值(如果不存在)。

对于“可选”,如果该值不存在,它将跳过所有后续操作。

有一些常见的操作,例如地图,过滤器,flatMap(这是Monad操作)以及其他更特定于Java的操作,例如Optional#orElseOptional#orElseThrow

要重构示例代码,您可以这样做。

void method() {
   return maybeGetThing().flatMap(val -> {

       // eek side effects
       val.doA();
       val.doB();
       val.doC();

       return val.maybeAnotherThing();
   });  
}

flatMap是一种将一种类型的Optional转换为另一种类型的Optional的方法。如果返回值不是Optional,则可以使用map。

您可以看到,为了命名lambda函数的参数,我们消除了对返回值名称的需求。 lambda函数的作用域是有限的,因此您可以根据需要重用名称。

我通常喜欢提供可运行的代码,所以这是我的意思是可运行的示例。

import java.util.Optional;

class DummyClass {

    private int val = 0;

    public void doA(){ val += 1; }

    public void doB(){ val += 2; }

    public void doC(){ val += 3; }

    public Optional<String> maybeAnotherThing(){
        return Optional.of(Integer.toString(val));
    }
}

public class UseOptional5 {   

    Optional<DummyClass> maybeGetThing(){
        return Optional.of(new DummyClass());
    }

    String method() {
        return maybeGetThing()
               // you can put other operations here
               .flatMap(val -> {

                    // eek side effects
                    val.doA();
                    val.doB();
                    val.doC();

                    return val.maybeAnotherThing();
                })
                // you can put other operations here too
                .orElseThrow(() -> new IllegalArgumentException("fail!!"));
    }    

    public static void main(String args[]) {

        UseOptional5 x = new UseOptional5();

        System.out.println(x.method());
    }
}

答案 1 :(得分:0)

自从我开始使用Java 9

void method() {
   maybeGetThing().ifPresentOrElse(
           usefulVariableName -> {
               usefulVariableName.doA();
               usefulVariableName.doB();
               usefulVariableName.doC();

               // No duplicate local variable opt
               Optional<ThingB> opt = usefulVariableName.maybeAnotherThing();
           },
           this::doSomethingOnlyHere
   );
}

我的经验法则是您很少需要或不想使用isPresent和/或get,它们是低级的。对于基本情况,ifPresent(带有f)和ifPresetnOrElse很好。其他人是正确的,mapflatMap也非常有用。