exceptions + signaling end-of-iterator: why is it bad in Java and normal in Python?
我真的很困惑:Java中的标准方法是只在"异常"条件下抛出异常,而不使用它们来指示迭代器的结束。
示例:有效Java、项目57("仅使用异常条件")和JavaPix主义者通讯162:
Flow control
We should never cause an exception that is otherwise preventable. I have seen code where instead of checking bounds, it is assumed that the data will be correct and then RuntimeExceptions are caught:
Here is an example of bad code (please don't code like this):
1
2
3
4
5
6
7
8
9
10
11
12 public class Antipattern1 {
public static void main(String[] args) {
try {
int i = 0;
while (true) {
System.out.println(args[i++]);
}
} catch (ArrayIndexOutOfBoundsException e) {
// we are done
}
}
}
而在python中使用这个习语是标准的,例如stopIteration:
exception StopIteration
Raised by an iterator‘s next() method to signal that there are no further values. This is derived from Exception rather than StandardError, since this is not considered an error in its normal application.
为什么它对Java有害,但对Python有好处呢?
Python和Java对异常有着截然不同的方法。在Python中,异常是正常的。在python词汇表中查找EAFP(请求宽恕比请求许可更容易)。还要检查维基百科要说什么。
StopIteration只是EAFP的一个例子——只需继续从迭代器中获取下一个内容,如果失败,则处理错误。
如果代码在非本地出口中可读性更强,则在Python中使用异常。你不写支票,你只是处理失败,如果事情不能解决。这绝对没有什么可耻的,事实上这是鼓励的。与Java不同。
现在,对于StopIteration的特定情况:考虑生成器函数。
1 2 3 4 | def generator(): yield 1 print('Side effect') yield 2 |
为了支持某种
我发现python的语义——只有在需要下一个值时才计算——是最好的选择。
当然Java没有可恢复的生成器,所以很难在这里进行比较。但有一些轶事证据表明StopIteration比HasNext()更好地进行归纳。
没有什么能阻止您使用Java中的异常,它看起来很丑陋,至少对Java开发者来说是如此。
主要原因是异常的stacktrace很昂贵,也可能Java开发者可能更关注关于花费比Python开发人员更多的计算资源。
Java也是一种相当"干净"的语言——有些人会说是原教旨主义的。这就是它是一种好语言的原因之一。(见评论)
不管怎样。原教旨主义者(和一些普通人)认为,对正常流动使用异常并不是正确的方法…-)
但除此之外,最近的JVM检测到您正在生成大量stacktrace对于相同的代码点,并将实际抛出没有异常的异常"一段时间后",以加快速度。
它不推荐的原因是因为在Java中,异常处理通常是昂贵的处理和恢复。当抛出异常时,它会导致JVM回溯正在进行的操作,以便提供一个堆栈跟踪,这对性能来说从来都不是一件好事。简而言之,这是语言滥用——通常会有一种更干净、更有效的逻辑处理方法。请考虑以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | try { int x = service.getValue(); if(x > 5) throw new NumberTooBigException("Too Big"); else throw new NumberTooSmallException("Too Small"); } catch (NumberTooBigException e) { System.out.println("i think it's too big..."); } catch (NumberTooSmallException e) { System.out.println("i think it's too small..."); } |
更好的方法是只使用Java的预期控制逻辑:
1 2 3 4 |
从这两个片段的比较中可以看出,异常处理有点荒谬——对样本要做的事情来说太过分了。对于您发布的示例,更好的方法如下:
当事情真的出了问题时,例如IOException("设备上没有剩余空间")、ClassNotFoundException("找不到要运行的代码")或NorouteToHosteException("无法连接到主机"),异常会更好地被使用。
Java有时也会这样做:"EDCOX1的0种方法的所有实现都使用EDCOX1×1"代替返回值。"在这种情况下,没有办法使用通常的前哨值EDCOX1×2"。
Java中的异常捕获执行堆栈,这极为缓慢:Java异常有多慢?
如果在控制流中使用exception,则将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
对此没有正确或错误的答案。异常处理是一个中立的控制流构造,它的最佳使用取决于上下文和样式。
在这种情况下,Java和Python都做同样的事情,因为同样的原因:EDCOX1×10的EDCOX1(4)使用EDCOX1对12的迭代器的信号端。这在两种语言中都是很好的风格:由于各种原因,使用特殊的sentinel返回值的替代方法要差得多。
作为经验法则,每当编写一个函数想要向多个类型的控制流返回发送信号时,应该考虑使用异常。这包括异常错误情况,但正确使用异常信号当然不限于此。
使用一个异常来实现这是一个设计选择,并且它并不是与Java异常心态不一致。当没有更多的元素时调用迭代器的