为什么不应该在参数中使用 Java 8 的 Optional
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31922866/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why should Java 8's Optional not be used in arguments
提问by Neil Stevens
I've read on many Web sites Optional should be used as a return type only, and not used in method arguments. I'm struggling to find a logical reason why. For example I have a piece of logic which has 2 optional parameters. Therefore I think it would make sense to write my method signature like this (solution 1):
我在很多网站上都读过 Optional 应该只用作返回类型,而不是在方法参数中使用。我正在努力寻找一个合乎逻辑的原因。例如,我有一段逻辑,它有 2 个可选参数。因此,我认为像这样编写我的方法签名是有意义的(解决方案 1):
public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
// my logic
}
Many web pages specify Optional should not be used as method arguments. With this in mind, I could use the following method signature and add a clear Javadoc comment to specify that the arguments may be null, hoping future maintainers will read the Javadoc and therefore always carry out null checks prior to using the arguments (solution 2):
许多网页指定 Optional 不应用作方法参数。考虑到这一点,我可以使用以下方法签名并添加明确的 Javadoc 注释来指定参数可能为空,希望未来的维护者能够阅读 Javadoc,因此在使用参数之前始终执行空检查(解决方案 2) :
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Alternatively I could replace my method with four public methods to provide a nicer interface and make it more obvious p1 and p2 are optional (solution 3):
或者,我可以用四个公共方法替换我的方法,以提供更好的接口并使其更明显 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
}
Now I try writing the code of the class which invokes this piece of logic for each approach. I first retrieve the two input parameters from another object which returns Optional
s and then, I invoke calculateSomething
. Therefore, if solution 1 is used the calling code would look like this:
现在我尝试编写为每种方法调用这段逻辑的类的代码。我首先从另一个返回Optional
s 的对象中检索两个输入参数,然后调用calculateSomething
. 因此,如果使用解决方案 1,调用代码将如下所示:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);
if solution 2 is used, the calling code would look like this:
如果使用解决方案 2,调用代码将如下所示:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
if solution 3 is applied, I could use the code above or I could use the following (but it's significantly more code):
如果应用解决方案 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();
}
}
So my question is: Why is it considered bad practice to use Optional
s as method arguments (see solution 1)?It looks like the most readable solution to me and makes it most obvious that the parameters could be empty/null to future maintainers. (I'm aware the designers of Optional
intended it to only be used as a return type, but I can't find any logical reasons not to use it in this scenario).
所以我的问题是:为什么将Optional
s 用作方法参数被认为是不好的做法(请参阅解决方案 1)?这对我来说似乎是最易读的解决方案,并且对于未来的维护者来说,参数可能为空/空是最明显的。(我知道设计者Optional
只打算将它用作返回类型,但我找不到任何不在这种情况下使用它的合乎逻辑的理由)。
采纳答案by Joop Eggen
Oh, those coding styles are to be taken with a bit of salt.
哦,这些编码风格需要加点盐。
- (+) Passing an Optional result to another method, without any semantic analysis; leaving that to the method, is quite alright.
- (-) Using Optional parameters causing conditional logic inside the methods is literally contra-productive.
- (-) Needing to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.
- (-) In comparison to nullable parameters Optional is more costly.
- (+) 将一个 Optional 结果传递给另一个方法,没有任何语义分析;把它留给方法,是很不错的。
- (-) 在方法中使用导致条件逻辑的可选参数实际上是适得其反的。
- (-) 需要在 Optional 中打包参数,对于编译器来说是次优的,并且会进行不必要的包装。
- (-) 与可为空的参数相比,Optional 的成本更高。
In general: Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.
一般而言: Optional 统一了两个必须解开的状态。因此,对于数据流的复杂性,结果比输入更适合。
回答by Danil Gaponov
I think that is because you usually write your functions to manipulate data, and then lift it to Optional
using map
and similar functions. This adds the default Optional
behavior to it.
Of course, there might be cases, when it is necessary to write your own auxilary function that works on Optional
.
我认为这是因为您通常编写函数来操作数据,然后将其提升为Optional
使用map
和类似的函数。这会Optional
为其添加默认行为。当然,在某些情况下,可能需要编写自己的适用于Optional
.
回答by Macchiatow
I believe the reson of being is you have to first check whether or not Optional is null itself and then try to evaluate value it wraps. Too many unnecessary validations.
我相信存在的原因是您必须首先检查 Optional 本身是否为 null,然后尝试评估它包装的值。太多不必要的验证。
回答by Makoto
The pattern with Optional
is for one to avoid returningnull
. It's still perfectly possible to pass in null
to a method.
with 的模式Optional
是为了避免返回null
。仍然完全有可能传入null
一个方法。
While these aren't really official yet, you can use JSR-308 styleannotations to indicate whether or not you accept null
values into the function. Note that you'd have to have the right tooling to actually identify it, and it'd provide more of a static check than an enforceable runtime policy, but it would help.
虽然这些还不是正式的,但您可以使用JSR-308 样式注释来指示您是否接受null
函数中的值。请注意,您必须拥有正确的工具来实际识别它,并且它提供的静态检查比可执行的运行时策略更多,但它会有所帮助。
public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}
回答by llogiq
This advice is a variant of the "be as unspecific as possible regarding inputs and as specific as possible regarding outputs" rule of thumb.
该建议是“输入时尽可能不具体,输出时尽可能具体”经验法则的变体。
Usually if you have a method that takes a plain non-null value, you can map it over the Optional
, so the plain version is strictly more unspecific regarding inputs. Howeverthere are a bunch of possible reasons why you would want to require an Optional
argument nonetheless:
通常,如果您有一个采用纯非空值的方法,您可以将其映射到 上Optional
,因此纯版本在输入方面更加不具体。但是,您Optional
仍然需要一个参数有很多可能的原因:
- you want your function to be used in conjunction with another API that returns an
Optional
- Your function should return something other than an empty
Optional
if the given value is empty You thinkOptional
is so awesome that whoever uses your API should be required to learn about it ;-)
- 您希望您的函数与另一个返回值的 API 结合使用
Optional
Optional
如果给定值为空,您的函数应返回空值以外的其他值你认为Optional
太棒了,应该要求使用你的 API 的人了解它 ;-)
回答by Steve B.
This seems a bit silly to me, but the only reason I can think of is that object arguments in method parameters already are optional in a way - they can be null. Therefore forcing someone to take an existing object and wrap it in an optional is sort of pointless.
这对我来说似乎有点傻,但我能想到的唯一原因是方法参数中的对象参数在某种程度上已经是可选的 - 它们可以为空。因此,强迫某人获取现有对象并将其包装在一个可选项中是毫无意义的。
That being said, chaining methods together that take/return optionals is a reasonable thing to do, e.g. Maybe monad.
话虽如此,将获取/返回选项的方法链接在一起是合理的做法,例如,Maybe monad。
回答by Kieran
Optionals aren't designed for this purpose, as explained nicely by Brian Goetz.
Optionals 不是为此目的而设计的,正如Brian Goetz很好地解释的那样。
You can always use @Nullableto denote that a method argument can be null. Using an optional does not really enable you to write your method logic more neatly.
您始终可以使用@Nullable来表示方法参数可以为 null。使用 optional 并不能真正让您更整洁地编写方法逻辑。
回答by Mark Perry
There are almost no good reasons for not using Optional
as parameters. The arguments against this rely on arguments from authority (see Brian Goetz - his argument is we can't enforce non null optionals) or that the Optional
arguments may be null (essentially the same argument). Of course, any reference in Java can be null, we need to encourage rules being enforced by the compiler, not programmers memory (which is problematic and does not scale).
几乎没有充分的理由不使用Optional
作为参数。反对这一点的论点依赖于权威的论点(请参阅 Brian Goetz - 他的论点是我们不能强制执行非空选项)或者Optional
参数可能为空(本质上是相同的论点)。当然,Java 中的任何引用都可以为 null,我们需要鼓励由编译器执行的规则,而不是程序员的内存(这是有问题的并且无法扩展)。
Functional programming languages encourage Optional
parameters. One of the best ways of using this is to have multiple optional parameters and using liftM2
to use a function assuming the parameters are not empty and returning an optional (see http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/data/Option.html#liftM2-fj.F-). Java 8 has unfortunately implemented a very limited library supporting optional.
函数式编程语言鼓励Optional
参数。使用它的最佳方法之一是拥有多个可选参数并使用liftM2
假设参数不为空并返回可选参数的函数(参见http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/数据/Option.html#liftM2-fj.F-)。不幸的是,Java 8 实现了一个非常有限的支持可选的库。
As Java programmers we should only be using null to interact with legacy libraries.
作为 Java 程序员,我们应该只使用 null 与遗留库进行交互。
回答by Gili
The best postI've seen on the topic was written by Daniel Olszewski:
我在这个主题上看到的最好的帖子是Daniel Olszewski写的:
Although it might be tempting to consider Optional for not mandatory method parameters, such a solution pale in comparison with other possible alternatives. To illustrate the problem, examine the following constructor declaration:
public SystemMessage(String title, String content, Optional<Attachment> attachment) { // assigning field values }
At first glance it may look as a right design decision. After all, we explicitly marked the attachment parameter as optional. However, as for calling the constructor, client code can become a little bit clumsy.
SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty()); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));
Instead of providing clarity, the factory methods of the Optional class only distract the reader. Note there's only one optional parameter, but imagine having two or three. Uncle Bob definitely wouldn't be proud of such code
When a method can accept optional parameters, it's preferable to adopt the well-proven approach and design such case using method overloading.In the example of the SystemMessage class, declaring two separate constructors are superior to using Optional.
public SystemMessage(String title, String content) { this(title, content, null); } public SystemMessage(String title, String content, Attachment attachment) { // assigning field values }
That change makes client code much simpler and easier to read.
SystemMessage withoutAttachment = new SystemMessage("title", "content"); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
尽管将 Optional 考虑为非强制性方法参数可能很诱人,但与其他可能的替代方案相比,这种解决方案显得苍白无力。为了说明问题,请检查以下构造函数声明:
public SystemMessage(String title, String content, Optional<Attachment> attachment) { // assigning field values }
乍一看,这可能是一个正确的设计决策。毕竟,我们明确地将附件参数标记为可选。但是,对于调用构造函数,客户端代码可能会变得有点笨拙。
SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty()); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));
Optional 类的工厂方法没有提供清晰的信息,只会分散读者的注意力。注意只有一个可选参数,但想象一下有两个或三个。鲍勃叔叔绝对不会为这样的代码感到自豪
当一个方法可以接受可选参数时,最好采用经过充分验证的方法并使用方法重载来设计这种情况。在 SystemMessage 类的示例中,声明两个单独的构造函数优于使用 Optional。
public SystemMessage(String title, String content) { this(title, content, null); } public SystemMessage(String title, String content, Attachment attachment) { // assigning field values }
这种变化使客户端代码更简单、更易于阅读。
SystemMessage withoutAttachment = new SystemMessage("title", "content"); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", attachment);
回答by Swaraj Yadav
One more approach, what you can do is
另一种方法,你可以做的是
// 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}
Once you have built a function(supplier in this case) you will be able to pass this around as any other variable and would be able to call it using
一旦你构建了一个函数(在这种情况下是供应商),你就可以将它作为任何其他变量传递,并且可以使用
calculatedValueSupplier.apply();
The idea here being whether you have got optional value or not will be internal detail of your function and will not be in parameter. Thinking functions when thinking about optional as parameter is actually very useful technique that I have found.
这里的想法是你是否有可选值将是你的函数的内部细节,而不是参数。在考虑可选作为参数时考虑函数实际上是我发现的非常有用的技术。
As to your question whether you should actually do it or not is based on your preference, but as others said it makes your API ugly to say the least.
至于您是否应该真正这样做的问题取决于您的偏好,但正如其他人所说,至少可以说它使您的 API 变得丑陋。