为什么不应该在参数中使用 Java 8 的 Optional

新手上路,请多包涵

我在很多网站上都读过 Optional 应该只用作返回类型,而不是在方法参数中使用。我正在努力寻找合乎逻辑的原因。例如,我有一段逻辑有 2 个可选参数。因此,我认为像这样编写我的方法签名是有意义的(解决方案 1):

 public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
    // my logic
}

许多网页指定 Optional 不应该用作方法参数。考虑到这一点,我可以使用以下方法签名并添加明确的 Javadoc 注释以指定参数可能为空,希望未来的维护者将阅读 Javadoc 并因此在使用参数之前始终执行空检查(解决方案 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 s 的对象中检索两个输入参数,然后调用 calculateSomething 。因此,如果使用解决方案 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();
    }
}

所以我的问题是:为什么使用 Optional s 作为方法参数被认为是不好的做法(参见解决方案 1)? 对我来说,它看起来是最易读的解决方案,并且最明显的是,对于未来的维护者来说,参数可能为空/空。 (我知道 Optional 的设计者打算将它仅用作返回类型,但我找不到任何合乎逻辑的理由不在这种情况下使用它)。

原文由 Neil Stevens 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 523
2 个回答

哦,那些编码风格要加一点盐。

  1. (+) 将 Optional 结果传递给另一个方法,不进行任何语义分析;把它留给方法,很好。
  2. (-) 在方法内部使用导致条件逻辑的可选参数实际上是适得其反的。
  3. (-) 需要在 Optional 中打包一个参数,这对编译器来说不是最优的,并且会进行不必要的包装。
  4. (-) 与可空参数相比,Optional 的成本更高。
  5. (-) 有人在实际参数中将 Optional 作为 null 传递的风险。

一般来说:Optional 统一了两个状态,这两个状态必须被分解。因此,由于数据流的复杂性,结果比输入更适合。

原文由 Joop Eggen 发布,翻译遵循 CC BY-SA 4.0 许可协议

我在该主题上看到的 最好的帖子 是由 Daniel Olszewski 撰写的:

尽管对于非强制方法参数考虑 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 类的工厂方法没有提供清晰度,只会分散读者的注意力。请注意只有一个可选参数,但想象一下有两个或三个。 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);

原文由 Gili 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题