我在许多网站上读到过,Optional应该只用作返回类型,而不能用于方法参数中。我在努力寻找一个合乎逻辑的原因。例如,我有一个逻辑,它有两个可选参数。因此,我认为这样写我的方法签名是有意义的(解决方案1):

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2) {
    // my logic
}

许多网页指定Optional不应该用作方法参数。考虑到这一点,我可以使用下面的方法签名,并添加一个明确的Javadoc注释来指定参数可能为null,希望未来的维护者能够读取Javadoc,因此在使用参数之前总是执行null检查(解决方案2):

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

或者,我可以用四个公共方法替换我的方法,以提供更好的接口,并使p1和p2是可选的(解决方案3):

public int calculateSomething() {
    calculateSomething(null, null);
}

public int calculateSomething(String p1) {
    calculateSomething(p1, null);
}

public int calculateSomething(BigDecimal p2) {
    calculateSomething(null, p2);
}

public int calculateSomething(String p1, BigDecimal p2) {
    // my logic
}

现在,我尝试编写为每种方法调用这段逻辑的类的代码。我首先从另一个返回Optionals的对象检索两个输入参数,然后调用calculatessomething。因此,如果使用解决方案1,调用代码将看起来像这样:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);

如果使用解决方案2,调用代码看起来像这样:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));

如果应用解决方案3,我可以使用上面的代码,也可以使用下面的代码(但它的代码明显更多):

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p1, p2);
    } else {
        result = myObject.calculateSomething(p1);
    }
} else {
    if (p2.isPresent()) {
        result = myObject.calculateSomething(p2);
    } else {
        result = myObject.calculateSomething();
    }
}

所以我的问题是:为什么使用可选项作为方法参数被认为是不好的做法(参见解决方案1)?对我来说,这看起来是最易读的解决方案,并且对于未来的维护者来说,参数可以是空的/null是最明显的。(我知道Optional的设计者只打算将其用作返回类型,但我找不到在这种情况下不使用它的任何逻辑理由)。


当前回答

我知道这个问题更多的是意见而不是事实。但是我最近从一个。net开发人员转变为java开发人员,所以我最近才加入了可选的一方。此外,我更喜欢把它作为一个评论,但由于我的分数水平不允许我评论,我被迫把它作为一个答案。

我一直在做的,这是我的经验之谈。是使用可选项作为返回类型,并且仅使用可选项作为参数,如果我需要可选项的值,以及可选项是否在方法中有值。

如果我只关心值,我在调用方法之前检查isPresent,如果我在方法中有某种日志记录或不同的逻辑,这取决于值是否存在,那么我将愉快地传入Optional。

其他回答

接受Optional作为参数会在调用者级别引起不必要的包装。

例如:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

假设您有两个非空字符串(即。从其他方法返回):

String p1 = "p1"; 
String p2 = "p2";

即使您知道它们不是空的,也必须将它们包装在Optional中。

当你必须与其他“可映射”结构组合时,情况会变得更糟。要么:

Either<Error, String> value = compute().right().map((s) -> calculateSomething(
< here you have to wrap the parameter in a Optional even if you know it's a 
  string >));

ref:

方法不应该期望Option作为参数,这几乎总是一个 指示从调用方到的控制流泄漏的代码气味 对于被呼叫者,应由呼叫者负责检查 期权内容

引用https://github.com/teamdigitale/digital-citizenship-functions/pull/148 # discussion_r170862749

首先,如果你使用方法3,你可以用下面的代码替换最后14行代码:

int result = myobject . calculatessomething (p1.orElse(null), p2.orElse(null));

您编写的四个变体是方便的方法。你应该只在更方便的时候使用它们。这也是最好的方法。这样,API就非常清楚哪些成员是必要的,哪些不是。如果你不想写四个方法,你可以通过参数的命名来澄清:

(String p1OrNull, BigDecimal p2OrNull)

这样,就很清楚允许空值。

p1.orElse(null)的使用说明了我们的代码在使用Optional时是多么冗长,这也是我避免使用它的部分原因。Optional是为函数式编程而编写的。流需要它。你的方法可能永远不应该返回Optional,除非在函数式编程中有必要使用它们。有一些方法,如option . flatmap()方法,需要对返回Optional的函数的引用。这是它的签名:

public <U>可选<U> flatMap(Function<?超级T, ?扩展了可选的< ?扩展U>>映射器)

这通常是编写返回Optional的方法的唯一理由。但即使在那里,也可以避免。您可以将一个不返回Optional的getter传递给flatMap()这样的方法,方法是将它包装在另一个将函数转换为正确类型的方法中。包装器方法看起来像这样:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) {
    return t -> Optional.ofNullable(function.apply(t));
}

假设你有一个这样的getter:

你不能像这样把它传递给flatMap:

opt.flatMap(Widget::getName) //不工作!

但你可以这样传递:

opt.flatMap(小部件::getter) //工作得很好!

在函数式编程之外,应该避免使用可选项。

Brian Goetz说得很好:

将Optional添加到Java的原因是:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .findFirst()
    .getOrThrow(() -> new InternalError(...));

比这个更干净:

Method matching =
    Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods())
    .stream()
    .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName())
    .filter(m ->  Arrays.equals(m.getParameterTypes(), parameterClasses))
    .filter(m -> Objects.equals(m.getReturnType(), returnType))
    .getFirst();
if (matching == null)
  throw new InternalError("Enclosing method not found");
return matching;

几乎没有理由不使用Optional作为参数。反对这一点的论点依赖于权威的论点(见Brian Goetz -他的论点是我们不能强制非空可选参数)或可选参数可能为空(本质上是相同的论点)。当然,Java中的任何引用都可以为空,我们需要鼓励规则由编译器执行,而不是由程序员的内存执行(这是有问题的,并且不能扩展)。

函数式编程语言鼓励可选参数。使用它的最佳方法之一是有多个可选参数,并使用liftM2来使用一个函数,假设参数不是空的并返回一个可选参数(参见http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/data/Option.html#liftM2-fj.F-)。不幸的是,Java 8实现了一个非常有限的库,支持可选功能。

作为Java程序员,我们应该只使用null来与遗留库交互。

我知道这个问题更多的是意见而不是事实。但是我最近从一个。net开发人员转变为java开发人员,所以我最近才加入了可选的一方。此外,我更喜欢把它作为一个评论,但由于我的分数水平不允许我评论,我被迫把它作为一个答案。

我一直在做的,这是我的经验之谈。是使用可选项作为返回类型,并且仅使用可选项作为参数,如果我需要可选项的值,以及可选项是否在方法中有值。

如果我只关心值,我在调用方法之前检查isPresent,如果我在方法中有某种日志记录或不同的逻辑,这取决于值是否存在,那么我将愉快地传入Optional。

另一个在传递Optional作为参数时要小心的原因是方法应该做一件事…如果你传递一个可选参数,你可以做不止一件事,它可以类似于传递一个布尔参数。

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }