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


当前回答

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

其他回答

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

正如Brian Goetz所解释的那样,可选选项并不是为此目的而设计的。

你总是可以使用@Nullable来表示方法参数可以为空。使用可选方法并不能让你的方法逻辑写得更整齐。

所以,如果你允许这个双关语,甲骨文发出了一个预言:

除函数返回值外,不应使用Optional。

我很喜欢到目前为止大多数的答案都与甲骨文的预言一致,在这个问题中提到的“许多网站”中,它在互联网上毫无疑问地重复着。这是非常典型的堆栈溢出:如果某件事据称应该是某种方式,你问为什么它应该是这样,几乎每个人都会给出原因;几乎没有人会质疑事实上是否应该如此。

所以,这里有一个不同的意见:

您可以使用Optional从代码库中完全消除null。

我曾经在一个100k行代码的项目中做到过。它工作。

If you decide to go along this path, then you will need to be thorough, so you will have a lot of work to do. The example mentioned in the accepted answer with Optional.ofNulable() should never occur, because if you are thorough, then you should never have anything returning null, and therefore no need for Optional.ofNullable(). In that 100k-lines-of-code project that I mentioned above, I have only used Optional.ofNullable() a couple of times when receiving results from external methods that I have no control over.

此外,如果您决定沿着这条路径走,您的解决方案将不是性能最好的解决方案,因为您将分配大量的可选选项。然而:

这只是运行时性能开销方面的缺点。 这并不是一个严重的劣势。 这是Java的问题,不是你的问题。

让我来解释一下最后一部分。

Java不像c#那样提供显式的引用类型可空性(从8.0版开始),所以在这方面它是劣势。(我说的是“在这方面”;在其他方面,Java更好;但现在这已经跑题了。)

对于引用类型的显式空性,唯一合适的替代方法是可选类型。

(它甚至可以说是稍微好一点,因为使用Optional你可以指明Optional -of- Optional,如果你必须,而使用显式的nullability你不能有ReferenceType??,或者至少你不能在c#中实现。)

可选的不需要增加开销,它只在Java中这样做。这是因为Java也不像c#和Scala那样支持真正的值类型。在这方面,Java远远不如这些语言。(再说一次,我说的是“在这方面”;在其他方面,Java更好;但现在这已经跑题了。)如果Java确实支持真值类型,那么Optional将被实现为单个机器单词,这意味着使用它的运行时开销将为零。

所以,问题归结起来就是:你想在你的代码中完美的清晰和类型安全,还是你更喜欢最大的性能?我相信对于高级语言(Java当然是其中之一),这个问题很久以前就解决了。

起初,我也倾向于将optional作为参数传递,但如果从API-Designer透视图切换到API-User透视图,就会看到缺点。

对于你的例子,每个参数都是可选的,我建议将计算方法更改为一个自己的类,如下所示:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();

我相信存在的原因是你必须首先检查Optional本身是否为null,然后尝试计算它包装的值。太多不必要的验证。