我正在写代码来寻找两条线的交点。当两条线的斜率相等时,它们不相交。但另一方面,斜率相等的输入值是完全有效的。
public static Point calculateIntersection(Line line1, Line line2) {
if (line1 == null || line2 == null) {
throw new NullPointerException(" some message ");
}
if (line1.getConstant() == line2.getConstant()) {
return new Point(0, line1.getConstant());
}
if (line1.getSlope() == line2.getSlope()) {
throw new IllegalArgumentException("slopes are same, the lines do not intersect.");
}
int x = (line2.getConstant() - line1.getConstant()) / (line1.getSlope() - line2.getSlope());
int y = line1.getSlope() * x + line1.getConstant();
return new Point(x, y);
}
问题是抛出非法参数异常是正确的做法吗?因为输入是有效的,它不能完全说服我。
自定义异常是正确的做法吗?听起来是个不错的选择,但额外的意见会有所帮助。
谢啦,谢啦
6条答案
按热度按时间sd2nnvve1#
问题是 * 抛出非法参数异常是正确的做法吗?*
没有单一的“正确的事情要做”,这取决于你想/需要如何“框定”这个条件;即它是一个bug,用户输入错误,还是程序应该能够处理的东西?
IllegalArgumentException
就可以了。这就是异常的设计目的。(注意,这是一个 unchecked 异常,所以期望它不会被捕获/恢复。IllegalArgumentException
...而不是表示“两行相交”以外的其他内容所混淆的可能性。在某些上下文中,最好根本不抛出异常,但(IMO)这不是这些上下文中的一个。
抛出异常的替代方法是返回
null
或返回一个Point
值,该值 * 意味着 * 调用代码“没有这样的点”。null
,应用程序必须处理null
情况......否则将出现NPE。Point
示例可以用来表示“不是点“1。这并不是说你不能让这些替代方案发挥作用,只是在这种情况下,要做到这一点,* 很可能 * 需要更多的工作,而且 * 很可能 * 不会有切实的回报。
1 -我假设
Point
是java.awt.Point
或类似的。显然你 * 可以 * 定义和使用一个自定义的Point
类,它提供了一个“no such point”示例。但是这是有代价的。你需要处理代码在一些计算中意外使用“no such point”示例的情况。你可能又回到了开始的地方;即抛出异常!sirbozc52#
这几乎肯定不会抛出异常,因为使用任意两个
Line
值调用这样的方法是非常有意义的,您已经适当地处理了空值。你还非常合理地定义了类在一种定义不清的输入情况下的行为,即两条重合的“常量”(水平)线,在那里你返回那条线上
x=0
处的点,你应该为其他定义不清的输入情况类似地选择返回值:重合的垂直线、既不水平也不垂直的重合线以及不重合的平行线。在我看来,最后一种情况--不重合的平行线--最自然的结果是
null
,反映了没有交点的事实。然后由客户端来决定空交集是否保证异常、错误消息或任何东西。例如,提示用户要相交的线的交互式 shell 可能会打印错误消息并要求用户重试。一些更复杂的计算,例如,线性优化器试图定义其搜索的边界,如果引起平行线的约束彼此矛盾,则可能想要抛出
IllegalArgumentException
。当然,所有这些情况下的返回值(重合线或非重合平行线)都应该精确地记录在方法的javadoc中。
ih99xse13#
我觉得你做得对你很早就发现了这个条件。要么是这样,要么人们会抱怨“你的程序有缺陷,看看这个输入数据,除以0”。
假设在99%以上的情况下不会出现这样的错误,这是一个异常条件,并且不允许声明一个已检查异常,因此未检查异常看起来确实是正确的选择。
现在,至于
IllegalArgumentException
是否是“好的”,它至少是描述这种情况的最接近的例外...你可以,如果你觉得你有一个更好的名字,总是创建你自己的继承RuntimeException
。如果,另一方面,这种情况并不罕见,那么也许应该审查达到该函数的逻辑,以便从一开始就不遇到这种情况。
2ul0zpep4#
正如@安迪·洛瑞和@KamikazeCZ所说,这不应该是一个例外。
如果调用者返回一个指示没有交集的结果,那么这段代码可以决定它是否是无效输入,因为最终用户已经得到了适当的警告,或者他们可以处理(也许通过重新提示),或者抛出一个定制的异常。
那么,回到这个方法应该返回什么?某种标记值,与
indexOf
在集合库中返回-1的方式相同。返回null
是一个合理的标记。在Java 8中,您可以返回一个Optional<Point>
,以帮助提醒调用者可能没有正确的Point
。你还有另外一个问题:一条直线与自身的交点是什么(从数学上讲,两条线的交点要么是点,要么是1点,要么是无限多个点。)您可能需要能够返回 * 两个 * sentinel值,这在Java中更复杂。这个方法这次可以通过说“在多个答案的情况下,这个方法可能返回其中的任何一个”来回避这种情况,或者(我可能会这样做)“...返回离原点最近的Point”。
顺便说一句,这种想法很大程度上来自单元测试的思维模式:首先定义正确的答案应该是各种极端情况下的输入,然后再启动代码,并有点承诺自己一定的返回类型等。
最后:当使用
==
比较getSlope()
的结果时,注意浮点舍入错误。这可能是最好的方法,但仍然有问题。不过,假设(或舍入)交集为int
s的方式表明,在您的问题中可能有非常特殊的约束/假设。ubby3x7f5#
如果是我,我会返回
null
。在大多数情况下,你不应该使用异常处理作为流控制的一种形式。返回null
将有助于避免任何代码使用你的方法。Dont Use Exceptions For Flow Control
Should a retrieval method return 'null' or throw an exception when it can't produce the return value? [closed]
Are exceptions as control flow considered a serious antipattern? If so, Why?
Arguments for or against using Try/Catch as logical operators [closed]
6g8kf2rb6#
异常应该被用来捕捉程序流中的错误(“内部发生了什么”),而不是用来验证输入。我根本不会抛出异常。想想看,这不是“异常”的意思,因为用户输入两条斜率相等的线是完全正常的。