我在许多网站上读到过,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的设计者只打算将其用作返回类型,但我找不到在这种情况下不使用它的任何逻辑理由)。


当前回答

查看JDK10中的JavaDoc, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html,添加了一个API说明:

API注意: Optional主要用于明确需要表示“没有结果”以及使用null可能导致错误的情况下作为方法返回类型使用。

其他回答

我的看法是,可选的应该是一个单子,这些在Java中是不可想象的。

In functional programming you deal with pure and higher order functions that take and compose their arguments only based on their "business domain type". Composing functions that feed on, or whose computation should be reported to, the real-world (so called side effects) requires the application of functions that take care of automatically unpacking the values out of the monads representing the outside world (State, Configuration, Futures, Maybe, Either, Writer, etc...); this is called lifting. You can think of it as a kind of separation of concerns.

混合使用这两种抽象级别不利于可读性,所以最好避免使用。

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

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

查看JDK10中的JavaDoc, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html,添加了一个API说明:

API注意: Optional主要用于明确需要表示“没有结果”以及使用null可能导致错误的情况下作为方法返回类型使用。

我认为这是因为您通常编写函数来操作数据,然后使用map和类似的函数将其提升为可选。这将添加默认的可选行为。 当然,在某些情况下,有必要编写自己的可用于Optional的辅助函数。

这个建议是“对输入尽可能不具体,对输出尽可能具体”经验法则的一种变体。

通常,如果您有一个接受普通非空值的方法,您可以将其映射到Optional上,因此普通版本对于输入严格来说更加不特定。然而,有很多可能的原因可以解释为什么你需要一个Optional参数:

您希望您的函数与另一个返回Optional的API一起使用 如果给定值为空,函数应该返回一个空可选值以外的值 你认为Optional是如此棒,以至于任何使用你的API的人都应该被要求学习它;-)