【发布时间】:2011-12-09 14:53:39
【问题描述】:
我真的很困惑:Java 中的标准方法是仅在“异常”条件下抛出异常,而不是使用它们来表示迭代器结束。
示例:Effective Java,第 57 条(“仅在异常情况下使用异常”)和JavaSpecialists newsletter 162:
流量控制
我们绝不应该引发本来可以预防的异常。我已经看到代码不是检查边界,而是假设数据是正确的,然后捕获 RuntimeExceptions:
这是一个错误代码的例子(请不要这样编码):
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:
异常停止迭代
由迭代器的 next() 方法引发,表示没有其他值。这是从 Exception 而不是 StandardError 派生的,因为这在其正常应用程序中不被视为错误。
为什么它对 Java 不利而对 Python 有利?
【问题讨论】:
-
这两个规则都只是意见。背后也没有客观的技术原因。在一个部落内,意见往往会合并为一种社会规范,但在不同的部落中,可能会出现不同的规范。这只是一个例子。
-
@Jason S:不能代表 Python 方面,但我认为 "iteration over an array" 可能是一个不好的例子,用于您的问题(很好问题顺便说一句)。它不好的原因是在 try/catch 块中包装东西会阻止现代虚拟机进行很酷的优化。 Joshua Block 在Effective Java 中也指出“创建、抛出和捕获异常通常很昂贵”。所以从性能上看,它看起来真的很糟糕(布洛赫称它总体上是“可怕的”)。仅仅因为这个原因,它就使得 “使用异常的数组迭代” 对于 Java 来说似乎是个坏主意。
-
@user988052:在 Python 中,设置 try/except 块非常快,提升+捕获也非常快。两者都是一次性操作;当然,它比所有那些
if语句都要快,至少对于几个元素而言。 -
@Petr Viktorin:非常感谢。因此,如果它在 Python 下非常快,而在 Java 中的性能可能很糟糕(通过也阻止 JVM 优化启动,除了可能让 try/throw/catch 本身有点慢),那这似乎是一个很好的理由,为什么在这个 “数组迭代” 中,它在 Python 中很好但在 Java 中不好。
-
@user988052:真正的原因是可读性,防止TOCTTOU 错误和竞争条件,以及 EAFP 与鸭子打字配合得很好。 Python 更关心这些而不是性能。但这是一个不错的奖励:)