Java 中的標記中斷:有用的工具還是程式碼異味?
1. 概述
Java 自 Java 1.0 以來就支援標記的break
和標記的continue
,並且沒有後續更改,使它們成為 Java 控制流程庫的一致組成部分。它允許開發人員退出嵌套結構中的特定循環,但這是一個好的做法嗎?
這篇簡短的文章探討了它的機制和權衡,並簡要地將它與類似的語言進行了對比。
2. 機制
在我們開始檢查之前,讓我們先討論一下基礎知識。
未標記的break
和continue
關鍵字會終止最內層的switch
、 for
、 while
或do-while
語句,而標記的版本則會終止特定的、主要為最外層的語句:
outer: // <-- label
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
println(i + ", " + j);
if (j == 2) {
break outer; // exits outer loop
}
}
}
內部for
迴break outer
outer指令終止父for
循環,標記為“outer”
。輸出如下:
0, 0
0, 1
0, 2
3. 優點與缺點
掌握了基礎知識後,值得問的是,這些功能在實際編碼中的表現如何。對於瀏覽嵌套結構的開發人員來說,這些工具提供了一種誘人的捷徑,但它們的實用性也有所權衡。
讓我們探討一下它們在實踐中帶來的好處和麵臨的挑戰。
3.1.優點
標記的break
和continue
在特定場景中具有明顯的優勢。
首先,它們允許立即退出而不需要額外的標誌或條件,從而提高了嵌套循環的效率。例如,讓我們考慮搜尋嵌套list
:
List<List<String>> listOfLists = fetchedSomewhere();
boolean containsExists = false;
outer: for(List<String> parent : listOfLists) {
for (String child : parent) {
if (child.contains(target)) {
containsExists = true;
break outer;
}
}
}
這避免了冗餘迭代,節省了複雜搜尋的運行時間。
其次,它們授予細粒度的控制,使我們能夠針對深度嵌套結構中的特定循環,這可以簡化邏輯,否則需要多個變數。
3.2.缺點
然而,缺點削弱了它們的吸引力。由於標籤類似於 GOTO 跳轉,可讀性會受到影響,讓不熟悉程式碼的維護人員感到困惑。命名不當的標籤(例如, x:
而不是outer:
)會加劇這種情況。此外,維護變得更加困難;修改標記循環可能會破壞其依賴關係,尤其是在大型程式碼庫中。
重構,例如使用 Java 的 Stream API 提取方法或應用函數式程式設計風格,通常更清晰:
boolean containsExists = listOfLists.stream()
.flatMap(Collection::stream)
.anyMatch(child -> child.contains(target));
最終,雖然標記語句在特殊情況下大放異彩,但它們的權衡利弊促使現代 Java 開發人員轉向結構化替代方案。
4. 其他語言
Java 的標記break
和continue
特性在程式語言中並不是獨一無二的。讓我們看看最接近、最相似的例子。
4.1.科特林
第一個參考語言是 JVM 語言家族的最新成員,並且在開發人員中的受歡迎程度不斷上升:Kotlin。與 Java 類似,它從第一個穩定版本開始就實作了標記的break
和continue
功能。
在 Kotlin 中,帶有標籤的break
如下所示:
outer@ for (i in 1..5) {
for (j in 1..5) {
if (j == 2) break@outer
println("$i, $j")
}
}
語法與 Java 類似,不同之處在於末尾必須有@
字元。 IntelliJ 等 IDE 也為 Kotlin 中的標籤提供了一種特殊的顏色,與預設的 Java 標籤外觀相比,它更容易被識別。
4.2. JavaScript
第二種參考語言在很大程度上反映了 Java 的方法。
在 JavaScript 中,帶有標籤的break
如下所示:
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
if (j === 1) break outer;
console.log(i, j);
}
}
這種相似性可能並不令人驚訝,因為 JavaScript 的設計很大程度上借鑒了 C 和 Java。
5. 結論
在本文中,我們探討了 Java 標記的break
和continue
語句的機制(這是其 1.0 版本中的一項功能),並權衡了它們的優點和缺點。雖然它們在嵌套循環中提供了效率和控制,但它們對可讀性和維護的影響通常會使天平傾向於更清潔的替代方案,例如方法重建。
Kotlin 與 JavaScript 的比較揭示了它們共同的傳承,但現代實踐傾向於使用結構化程式碼而不再使用這種構造。對於 Java 開發人員來說,標記語句仍然是一種情境工具 - 最好保留用於不會犧牲清晰度的罕見、複雜場景。
與往常一樣,本文使用的完整程式碼可以在 GitHub 上找到。