我在许多网站上读到过,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作为参数时要小心的原因是方法应该做一件事…如果你传递一个可选参数,你可以做不止一件事,它可以类似于传递一个布尔参数。

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }

其他回答

这对我来说似乎有点傻,但我能想到的唯一原因是方法参数中的对象参数在某种程度上已经是可选的——它们可以为空。因此,强迫某人使用现有对象并将其包装为可选对象是毫无意义的。

也就是说,将可选的take/return方法链接在一起是一件合理的事情,例如monad。

我知道这个问题更多的是意见而不是事实。但是我最近从一个。net开发人员转变为java开发人员,所以我最近才加入了可选的一方。此外,我更喜欢把它作为一个评论,但由于我的分数水平不允许我评论,我被迫把它作为一个答案。

我一直在做的,这是我的经验之谈。是使用可选项作为返回类型,并且仅使用可选项作为参数,如果我需要可选项的值,以及可选项是否在方法中有值。

如果我只关心值,我在调用方法之前检查isPresent,如果我在方法中有某种日志记录或不同的逻辑,这取决于值是否存在,那么我将愉快地传入Optional。

起初,我也倾向于将optional作为参数传递,但如果从API-Designer透视图切换到API-User透视图,就会看到缺点。

对于你的例子,每个参数都是可选的,我建议将计算方法更改为一个自己的类,如下所示:

Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();

MyCalculator mc = new MyCalculator();
p1.map(mc::setP1);
p2.map(mc::setP2);
int result = mc.calculate();

另一个在传递Optional作为参数时要小心的原因是方法应该做一件事…如果你传递一个可选参数,你可以做不止一件事,它可以类似于传递一个布尔参数。

public void method(Optional<MyClass> param) {
     if(param.isPresent()) {
         //do something
     } else {
         //do some other
     }
 }

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

// 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丑陋,至少可以说。