为什么不应该在参数中使用Java8的Optional?
为什么不应该在参数中使用Java8的Optional?
我在许多网站上阅读过,Optional 应该仅用作返回类型,而不应在方法参数中使用。我正在努力找到一个逻辑上的理由。例如,我有一段逻辑,有两个可选参数。因此,我认为将方法签名写成这样是有道理的(解决方案1):
public int calculateSomething(Optional p1, Optional 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 }
现在,针对每种方法,我尝试编写调用此逻辑的类的代码。我首先从另一个返回 Optional 的对象中获取两个输入参数,然后调用 calculateSomething。因此,如果使用解决方案1,则调用代码将如下所示:
Optional p1 = otherObject.getP1(); Optional p2 = otherObject.getP2(); int result = myObject.calculateSomething(p1, p2);
如果使用解决方案2,则调用代码将如下所示:
Optional p1 = otherObject.getP1(); Optional p2 = otherObject.getP2(); int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
如果应用解决方案3,我可以使用上面的代码,或者我可以使用以下代码(但它的代码量显着更多):
Optional p1 = otherObject.getP1(); Optional 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(); } }
所以我的问题是:为什么认为将 Optional 用作方法参数是不好的做法(见解决方案1)? 对我来说,这看起来是最可读的解决方案,并且使未来的维护者更明显地知道参数可能为空/ null。(我知道 Optional 的设计者打算仅将其用作返回类型,但我找不到在此场景中不使用它的任何逻辑理由)。
这篇文章是我看过关于此主题最好的文章,作者是Daniel Olszewski:
虽然在非必需方法参数中使用Optional可能很诱人,但与其他可能的替代方案相比,这种解决方法相形见绌。为了阐明这个问题,请查看以下构造函数声明:
public SystemMessage(String title, String content, Optionalattachment) { // 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类的工厂方法只会分散读者的注意力,而不是提供清晰明了的信息。请注意,这里只有一个可选参数,但想象一下有两个或三个。Bob大叔绝对不会对这样的代码感到自豪。
当一个方法可以接受可选参数时,最好采用经过充分验证的方法重载的方式来设计这种情况。在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);