Golang 控制流程 – 條件判斷(if‑else、switch‑case)
簡介
在任何程式語言中,條件判斷都是決定程式執行路徑的核心機制。Go 語言以簡潔、直觀的語法提供 if‑else 與 switch‑case,讓開發者能在保持程式可讀性的同時,快速完成分支邏輯。
掌握這兩種結構不只可以寫出正確的程式,更能提升程式的可維護性與效能,對於從新手晉升為中級開發者尤為重要。以下我們將深入探討 Go 的條件判斷寫法、常見陷阱與實務應用。
核心概念
1. if 與 else 的基本語法
Go 的 if 語句不需要圓括號,條件表達式直接寫在 if 後;大括號則是必須的。
if x > 10 {
fmt.Println("大於 10")
}
若需要多條件或先宣告變數,可在 if 前加上簡短的 短變數宣告(short variable declaration):
if n := len(s); n == 0 {
fmt.Println("字串為空")
} else if n < 5 {
fmt.Println("字串長度小於 5")
} else {
fmt.Println("字串長度足夠")
}
重點:短變數宣告的作用域僅限於整個
if‑else區塊,結束後即被釋放,避免變數污染。
2. 多層 if‑else 的寫法
在 Go 中,else 必須與前一個 if 的右大括號同一行,否則會產生編譯錯誤:
if score >= 90 {
grade = "A"
} else if score >= 80 { // 正確寫法:else 必須接在 } 後面同一行
grade = "B"
} else {
grade = "C"
}
3. switch 的基本結構
switch 是多分支選擇的利器。Go 的 switch 不必寫 break,每個 case 執行完會自動跳出。若想要「fall‑through」必須顯式使用 fallthrough 關鍵字。
switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
fmt.Println("平日上班")
case "Sat", "Sun":
fmt.Println("週末休息")
default:
fmt.Println("未知的日期")
}
4. 表達式 switch(Switch without a condition)
省略條件式時,switch 會依序評估每個 case 的布林表達式,第一個為 true 的 case 會被執行,等同於 if‑else if‑else 鏈。
switch {
case age < 18:
fmt.Println("未成年")
case age >= 18 && age < 65:
fmt.Println("成年")
default:
fmt.Println("高齡")
}
5. type switch – 依型別分支
在需要根據變數的實際型別做不同處理時,可使用 type switch:
var v interface{} = getValue()
switch val := v.(type) {
case int:
fmt.Printf("整數 %d\n", val)
case string:
fmt.Printf("字串 %s\n", val)
default:
fmt.Printf("其他型別 %T\n", val)
}
技巧:
type switch常與interface{}搭配,用於 JSON 解析、通用函式等情境。
6. switch 中的 fallthrough
fallthrough 只會讓程式執行下一個 case 的語句,不會重新評估條件,因此使用時需特別小心。
switch n {
case 1:
fmt.Println("一")
fallthrough
case 2:
fmt.Println("二") // 會被執行,即使 n != 2
default:
fmt.Println("其他")
}
程式碼範例
範例 1:使用 if‑else 判斷使用者年齡
package main
import "fmt"
func main() {
var age int
fmt.Print("請輸入年齡: ")
fmt.Scan(&age)
if age < 0 {
fmt.Println("年齡不可為負")
} else if age < 18 {
fmt.Println("未成年")
} else if age < 65 {
fmt.Println("成年人")
} else {
fmt.Println("高齡族群")
}
}
說明:透過多層 else if,將年齡區間清晰分段,並在最前面先檢查不合理的負值。
範例 2:switch 判斷星期與工作排程
package main
import (
"fmt"
"time"
)
func main() {
today := time.Now().Weekday() // 取得當前星期
switch today {
case time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday:
fmt.Println("今天是上班日")
case time.Saturday:
fmt.Println("今天是加班日")
case time.Sunday:
fmt.Println("今天是休息日")
}
}
說明:利用 time.Weekday 常數直接對照,避免手寫字串比較,提高可讀性與安全性。
範例 3:表達式 switch 用於分級評分
package main
import "fmt"
func grade(score int) string {
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
case score >= 70:
return "C"
case score >= 60:
return "D"
default:
return "F"
}
}
func main() {
fmt.Println("成績 85 => 等級", grade(85))
}
說明:省去重複的變數比較,讓程式更簡潔;default 處理所有未符合條件的分數。
範例 4:type switch 解析任意型別的 JSON 欄位
package main
import (
"encoding/json"
"fmt"
)
func main() {
data := []byte(`{"id":123,"name":"Alice","active":true}`)
var m map[string]interface{}
json.Unmarshal(data, &m)
for k, v := range m {
switch val := v.(type) {
case float64: // json 的數字預設是 float64
fmt.Printf("%s 為數字: %.0f\n", k, val)
case string:
fmt.Printf("%s 為字串: %s\n", k, val)
case bool:
fmt.Printf("%s 為布林: %t\n", k, val)
default:
fmt.Printf("%s 為未知型別: %T\n", k, val)
}
}
}
說明:在處理動態結構(如 JSON)時,type switch 能快速分辨不同資料型別,避免手動斷言錯誤。
範例 5:switch 搭配 fallthrough 實作簡易的狀態機
package main
import "fmt"
func main() {
state := 1
switch state {
case 1:
fmt.Println("狀態 1:初始化")
fallthrough
case 2:
fmt.Println("狀態 2:載入資源")
fallthrough
case 3:
fmt.Println("狀態 3:執行中")
default:
fmt.Println("未知狀態")
}
}
說明:fallthrough 讓程式從狀態 1 直接跑到狀態 3,模擬「連續執行」的情境,但要注意 僅在確定需要此行為時使用,否則容易產生意外的邏輯錯誤。
常見陷阱與最佳實踐
| 陷阱 | 可能的問題 | 解決方案 / 最佳實踐 |
|---|---|---|
忘記 else 同行 |
編譯錯誤 expected else, found |
} else { 必須寫在同一行,或使用大括號包住整段 if‑else |
if 中的短變數作用域誤用 |
變數在外層不可見,導致未宣告錯誤 | 確認需要的變數是否真的只在條件內使用,若需外部使用,改為先宣告再賦值 |
switch 中忘記 break |
Go 會自動跳出,若想手動中斷請使用 return 或 goto |
了解 Go 的自動 break 行為,僅在真的需要 fallthrough 時才使用 |
fallthrough 濫用 |
造成不必要的 case 被執行,邏輯難以追蹤 | 僅在「必須」連續執行多個 case 時使用,並在註解說明原因 |
type switch 忽略 default |
未處理未知型別會導致 panic | 始終加入 default,即使只打印警告或回傳錯誤 |
在 switch 中使用複雜表達式 |
可讀性下降 | 若條件過於複雜,考慮改寫為 if‑else 或抽離為函式 |
其他最佳實踐
- 保持條件簡潔:每個
if或case只處理單一概念,避免「過長的條件」讓程式難以閱讀。 - 使用命名常數:對於
switch中的固定值(如狀態碼、類型代號),使用iota或const定義,提升可維護性。 - 避免在
case中寫大量程式:若需要執行多行邏輯,建議抽成獨立函式,再在case內呼叫。 - 測試邊界值:尤其是
if‑else的比較運算子(<,<=,>),務必寫測試覆蓋等於與不等於的情況。
實際應用場景
| 場景 | 為何使用 if‑else |
為何使用 switch |
|---|---|---|
| API 請求參數驗證 | 需要逐一檢查多個欄位是否為空、格式是否正確,條件較為分散 | 若參數類型或狀態碼固定,可用 switch 快速分支 |
| HTTP 狀態碼處理 | 只需要判斷 2xx、4xx、5xx 三大類,if 足以 |
若要對每個具體狀態碼(200、201、404、500)分別處理,switch 更直觀 |
| 業務規則引擎 | 複雜的條件組合(如金額 > X 且客戶等級 = A)適合 if 結合布林運算 |
若規則是「根據類別」的離散選擇(如商品類別),switch 更易維護 |
| 狀態機(State Machine) | 簡單的二元切換(開/關)可用 if |
多階段流程(Init → Load → Run → Stop)適合 switch + fallthrough 或 type switch |
| JSON/介面型別動態解析 | 需要先判斷是否為 nil、是否為字串等,if 先行檢查 |
真正根據型別分支時,type switch 為首選 |
總結
條件判斷是程式設計的基礎,也是控制流程的核心。Go 提供的 if‑else 與 switch‑case,在語法上盡量減少冗餘(不需要圓括號與 break),同時保留了足夠的彈性:
if‑else:適合少數條件、需要先行變數宣告或布林運算的情境。switch:適合多分支、離散值或型別判斷,且可透過 表達式switch與type switch處理更複雜的需求。
掌握上述概念、避免常見陷阱、遵循最佳實踐,能讓你寫出 可讀、可維護且效能良好 的 Go 程式。未來在開發 API、業務邏輯或大型系統時,條件判斷的正確運用將是提升開發效率與程式品質的關鍵。祝你在 Golang 的旅程中,寫出更乾淨、更可靠的程式碼!