为什么不应该在参数中使用Java8的Optional?

46 浏览
0 Comments

为什么不应该在参数中使用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 的设计者打算仅将其用作返回类型,但我找不到在此场景中不使用它的任何逻辑理由)。

admin 更改状态以发布 2023年5月22日
0
0 Comments

这篇文章是我看过关于此主题最好的文章,作者是Daniel Olszewski

虽然在非必需方法参数中使用Optional可能很诱人,但与其他可能的替代方案相比,这种解决方法相形见绌。为了阐明这个问题,请查看以下构造函数声明:

public SystemMessage(String title, String content, Optional 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);

0
0 Comments

哦,那些编码风格需要被谨慎对待。

  1. (+) 将可选结果传递给另一个方法而没有进行任何语义分析,让方法去处理是完全可以的。
  2. (-) 使用可选参数会导致方法内部出现条件逻辑,这对生产力是有害的。
  3. (-) 将参数打包在可选参数中对编译器来说并不是最佳选择,这样做会进行不必要的包装。
  4. (-) 相比可空参数,可选参数的成本更高。
  5. (-) 有人将可选参数作为实际参数传递为空的风险。

总的来说,可选参数统一了两种状态,并需要进行解决。因此,它比输入更适合作为结果的复杂数据流。

0