目前,我正在实施一个带有以下签名的方法
public static double Newtons_method(double[] payments, double[] days, double guess)
有时候,计算会失败。有几种方法可以表明失败。
Double
,并返回null。Double.NaN
并使用Double.isNaN(double)
对其进行测试。然而,有几个问题。
Double
,我需要做很多方法和拆箱。try
catch
会使代码看起来很混乱。不是,在Newtons_method
中,有很多常见的情况会导致此类算法失败。因此,将它们视为exception
第3种方法怎么样,返回Double.NaN
?这是一种好的做法吗?
以下是供您参考的源代码。
https://github.com/yccheok/xirr/blob/master/src/org/yccheok/quant/XIRR.java#L56
请注意,尚未执行任何错误检查。因此,err
可能永远不会进一步减少,并且该方法可能会进入无限循环。
答案 0 :(得分:5)
一般而言,在特殊情况下使用特殊返回值的做法违背了Java的方式,通过Java的方式通知调用者异常情况。为此,策略#1和#3是等效的,因为它们都会产生这样的调用者代码:
// This could be double or Double
double res = Newtons_method(pmt, guess);
// This could be res==null check or Double.isNaN(res) call
if (!checkResult(res)) {
// Do something else
}
// Do regular processing
这很容易出错,因为不可避免地会有人忘记检查,导致错误发生。当嵌套方法调用时,这往往会发生更多,如
double res = doStuff(Newtons_method(pmt1, guess), Newtons_method(pmt2, guess));
不正确的结果使它成为下一级函数调用的参数,强制参数异常(如果编码器非常善于他的参数检查)或者很难调试错误。
你的策略#2更好,因为来电者不能简单地忘记"捕获异常:他们必须捕获并处理它,或者将throws
添加到他们自己的方法中。
答案 1 :(得分:1)
这是一个结合了异常的安全网优势的解决方案,但不会强制调用者使用它们来检查成功或失败。如果调用者不想要这个成本,那么每次结果失败时它也没有抛出异常的成本:
public Result compute(...) { ... }
public interface Result {
/**
* Returns true if the computation was successful
*/
boolean isSuccessFul();
/**
* Returns the computed value if successful.
* @throws IllegalStateException if the computation failed - you could use another exception here if you prefer
*/
double getValue();
/**
* Gets the reason for the failure, is the computation has failed.
* @throws IllegalStateException if the computation succeeded
*/
String getFailureReason();
}
class Success implements Result {
private final double value;
public Success(double value) {
this.value = value;
}
@Override
public boolean isSuccessFul() {
return true;
}
@Override
public double getValue() {
return value;
}
@Override
public String getFailureReason() {
throw new IllegalStateException("The computation was successful");
}
}
class Failure implements Result {
private final String reason;
public Failure(String reason) {
this.reason = reason;
}
@Override
public boolean isSuccessFul() {
return false;
}
@Override
public double getValue() {
throw new IllegalStateException(reason);
}
@Override
public String getFailureReason() {
return reason;
}
}
这样,假设结果成功的调用者将会
double result = compute().getValue();
如果结果失败,将获得有意义的异常。
测试结果的人将能够在没有任何性能损失的情况下执行此操作:
Result r = compute();
if (r.isSuccessful()) {
double value = r.getValue();
...
}
else {
...
}
这与返回null基本类似,除了代码更加不言自明,调用者忘记测试失败案例的机会少得多,并且在这种情况下得到一个有意义的异常。
答案 2 :(得分:0)
我看到人们以类似的方式使用Double.NaN,而且我从未听过有人说这样做是不好的做法。您可能希望放入使用该方法的方法Javadoc。