似乎Groovy不支持在闭包内使用break
和continue
。有什么最好的方法可以模拟出这种行为吗?
revs.eachLine { line ->
if (line ==~ /-{28}/) {
// continue to next line...
}
}
您只能支持干净地继续,而不能打破。特别是对于像eachLine和each这样的东西。无法支持break是因为这些方法的评估方式,没有考虑不完成循环可以传达给方法的任何事情。以下是如何支持continue的方法--
最佳方法(假设您不需要结果值)。
revs.eachLine { line ->
if (line ==~ /-{28}/) {
return // returns from the closure
}
}
如果你的示例确实如此简单,那么这对于可读性来说是很好的。
revs.eachLine { line ->
if (!(line ==~ /-{28}/)) {
// do what you would normally do
}
}
另一个选项是,在字节码级别上模拟通常会发生的“继续”操作。
revs.eachLine { line ->
while (true) {
if (line ==~ /-{28}/) {
break
}
// rest of normal code
break
}
}
通过异常处理是支持break的一种可能方式:
try {
revs.eachLine { line ->
if (line ==~ /-{28}/) {
throw new Exception("Break")
}
}
} catch (Exception e) { } // just drop the exception
如果您的类中可能会抛出实际异常,如NumberFormatException或IOException,为避免掩盖其他真实异常,您可能希望使用自定义异常类型。
闭包不能使用break或continue语句,因为它们不是循环/迭代结构。相反,它们是处理/解释/处理迭代逻辑的工具。您可以通过仅从闭包返回而不进行处理来忽略给定的迭代,例如:
revs.eachLine { line ->
if (line ==~ /-{28}/) {
return
}
}
在闭包级别上并没有支持break,它是通过接受闭包的方法调用的语义隐含的。简而言之,这意味着你不应该像处理整个集合一样调用"each",而应该调用find,直到满足某个条件为止。大多数(全部?)情况下,当你需要从一个闭包中退出时,你真正想做的是在迭代过程中找到一个特定的条件,使得find方法不仅符合你的逻辑需求,还符合你的意图。不幸的是,有些API缺少对find方法的支持,例如File类。也许把时间花在争论是否应该包括break/continue上,可能不如把这些被忽视的领域添加find方法来得好。例如,像firstDirMatching(Closure c)或findLineMatching(Closure c)这样的方法可以很好地解决99+%的“为什么我无法从...中断?”的问题,这些问题经常出现在邮件列表中。话虽如此,你可以通过MetaClass或Categories轻松地自行添加这些方法。
class FileSupport {
public static String findLineMatching(File f, Closure c) {
f.withInputStream {
def r = new BufferedReader(new InputStreamReader(it))
for(def l = r.readLine(); null!=l; l = r.readLine())
if(c.call(l)) return l
return null
}
}
}
using(FileSupport) { new File("/home/me/some.txt").findLineMatching { line ==~ /-{28}/ }
把异常和其他神奇的东西结合起来可能会起作用,但在某些情况下会引入额外的开销,在其他情况下会使可读性变得复杂。真正的答案是查看您的代码,并询问自己是否真正进行的是迭代或搜索。
final static BREAK = new Exception();
//...
try {
... { throw BREAK; }
} catch (Exception ex) { /* ignored */ }
使用 return 来 继续执行,使用任何闭包来 中止执行。
示例
文件内容:
1
2
----------------------------
3
4
5
Groovy代码:
new FileReader('myfile.txt').any { line ->
if (line =~ /-+/)
return // continue
println line
if (line == "3")
true // break
}
输出:
1
2
3
.any.
。如果闭包中的最后一个语句与“true”相关联,它将停止执行。如果你想让逻辑或文件被完全处理,请把“false”作为任何闭包内的最后一个语句。我看到这篇文章之前花了几天时间。 - user2618844line == "3"
替换最后的if块,我认为会更加清晰。 - Ed Norris在这种情况下,你应该考虑使用find()
方法。它会在传递给它的闭包函数第一次返回true后停止执行。
import rx.Observable
Observable.from(1..100000000000000000)
.filter { it % 2 != 1}
.takeWhile { it<10 }
.forEach {println it}