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


当前回答

哦,这些编码风格应该有所保留。

(+)传递一个可选结果给另一个方法,没有任何语义分析;把它留给方法,是很好的。 (-)在方法中使用可选参数导致条件逻辑是反作用的。 (-)需要将参数打包到Optional中,对编译器来说是次优的,并且做了不必要的包装。 (-)与可空参数相比,Optional的开销更大。 (-)有人在实际参数中传递Optional为空的风险。

总的来说:可选的统一了两个状态,这两个状态必须被分解。因此,由于数据流的复杂性,它更适合于结果而不是输入。

其他回答

一个很好的例子是可选的参数会很好是JPA存储库。我喜欢做一些像findbynameand姓氏(可选,可选)。这样,如果Optional为空,则不执行WHERE param=y

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

让我们明确一点:在其他语言中,并没有反对将Maybe类型用作字段类型、构造函数形参类型、方法形参类型或函数形参类型的一般建议。

因此,如果您“不应该”在Java中使用Optional作为参数类型,原因是特定于Optional或Java或两者。

可能适用于其他Maybe类型或其他语言的推理在这里可能是无效的。

根据Brian Goetz,

[W]e did have a clear intention when adding [Optional], and it was not to be a general purpose Maybe type, as much as many people would have liked us to do so. Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors. For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter.

所以答案是针对Optional的:它不是“一般用途的可能类型”;因此,它是有限制的,而且限制的方式可能限制了它作为字段类型或参数类型的用处。

也就是说,在实践中,我很少发现使用Optional作为字段类型或参数类型是一个问题。如果Optional(尽管有局限性)作为参数类型或字段类型用于您的用例,请使用它。

还有一种方法,你能做的是

// get your optionals first
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

// bind values to a function
Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

一旦你构建了一个函数(在本例中是供应商),你就可以将它作为任何其他变量传递,并能够使用它来调用

calculatedValueSupplier.apply();

这里的想法是,你是否有可选值将是你的函数的内部细节,而不是参数。将可选参数作为参数来考虑函数实际上是我发现的非常有用的技巧。

至于你的问题,你是否应该这样做,这是基于你的偏好,但正如其他人所说,它使你的API丑陋,至少可以说。

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

除函数返回值外,不应使用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当然是其中之一),这个问题很久以前就解决了。