我真的很困惑:Java中的标准方法是仅在“异常”条件下抛出异常,而不是使用它们来表示迭代器结束。
示例:Effective Java,第57项(“仅针对特殊情况使用例外”)和 Java专家新闻通讯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:
例外StopIteration
由迭代器的next()方法引发,表示没有其他值。这是从Exception而不是StandardError派生的,因为这在其正常应用程序中不被视为错误。
为什么它对Java不好但对Python有好处?
Python和Java有很多不同的异常方法。在Python中,异常是正常的。抬头 EAFP (Python术语表中更容易请求宽恕而非许可)。还检查一下 维基百科 不得不说。
StopIteration只是EAFP的一个例子 - 只需继续从迭代器中获取下一个东西,如果失败,则处理错误。
如果代码使用非本地出口更具可读性,则在Python中使用异常。如果事情没有成功,你就不会写支票。这绝对没什么可耻的,事实上它是鼓励的。与Java不同。
现在针对StopIteration的特定情况:考虑一下 发电机功能。
def generator():
yield 1
print('Side effect')
yield 2
支持某种 has_next()
方法,生成器必须检查下一个值,触发 print
之前 2
被要求。必须在迭代器中记住值(或引发的异常)。如果 has_next
被叫两次,只有第一次会引发副作用。或者,即使不需要,也可以预先计算下一个值。
我发现了Python的语义 - 只有在需要下一个值时才进行计算 - 这是最好的选择。
当然Java没有可恢复的生成器,所以这里很难比较。但是有一些传闻证据表明StopIteration比hasNext()更好地概括。
没有什么 停止 你使用java中的异常,
它只是看起来很难看,至少对于java开发人员来说。
主要原因是来自异常的堆栈跟踪是昂贵的,
也可能是Java开发人员可能会稍微担心一下
关于花费计算资源比Python开发人员。
Java也是一种相当“干净”的语言 - 有人会说基础主义,
这是一个很好的语言的原因之一。 (*见评论)
无论如何。原教旨主义者(和一些普通人)认为使用正常流量的例外不是正确的方式... :-)
但除此之外,最近的jvm检测到你正在产生大量的堆栈跟踪
对于相同的代码点,并且实际上会在没有它们的情况下“暂时”抛出异常来加快速度。
Java有时候 可以也是:“所有的实现 DataInput
方法使用 EOFException
而不是返回值。“在这种情况下,没有办法使用通常的哨兵值, -1
。
不推荐它的原因是因为在java中,异常处理通常很难处理和恢复。当抛出异常时,它会导致jvm回溯正在进行的操作,以便提供堆栈跟踪,这对性能来说永远不是好事。简而言之,它是语言滥用 - 通常会有更清晰,更有效的逻辑处理方式。请考虑以下代码:
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的预期控制逻辑:
if(x > 5)
System.out.println("i think it's too big...");
else
System.out.println("i think it's too small...");
正如您从比较这两个片段中看到的那样,异常处理有点荒谬 - 对样本打算做什么有点过分。对于您发布的示例,更好的方法是这样的:
String[] args = {"one", "two", "three"};
for(String arg : args)
System.out.println(args);
}
当事情真的出错时,例外情况更好用,例如IOException(“设备上没有剩余空间”),ClassNotFoundException(“找不到要运行的代码”)或NoRouteToHostException(“无法连接到主机”)。
StopIteration
存在于Python中,可以在任何序列上进行简单迭代。
使用异常来实现这是一个设计选择,它实际上并不与Java的异常心态不一致。迭代器 next()
当没有更多元素时调用的方法是一个异常条件,并在一个场景中捕获该异常 for
循环是一种非常简单的方法来实现“在没有任何东西之前取物品”。
对此没有正确或错误的答案。异常处理是一种中立的控制流构造,它的最佳用途取决于上下文和样式。
在这种情况下,Java和Python都做同样的事情,原因相同: java.util.Iterator
的 next()
使用 NoSuchElementException
发信号通知结束。这在两种语言中都是很好的风格:出于各种原因,使用特殊哨兵返回值的替代方案要差得多。
根据经验,只要编写想要发出信号的函数,就应该考虑使用异常 不止一种控制流程返回。这包括异常错误情况,但是异常信令的良好使用当然不限于此。
Java中的异常捕获执行堆栈,这非常慢: Java异常有多慢?
如果在控制流程中使用异常 null
如 Throwable
。否则,将通过层次结构调用标准Java librery代码 super
要求:
public Throwable() {
fillInStackTrace();
}
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);