if/else、switch

May 21, 2022

現實生活中待解決的事千奇百怪,想使用電腦解決的需求也是各式各樣:「如果」發生了…,就要…;「對於」…,就一直執行…;「如果」…,就「中斷」…。為了告訴電腦特定條件該執行的動作,可使用條件式來定義程式執行流程。

if/else

Java 提供了 if/else 條件式,語法如下:

if(條件式) {
    陳述句;
}
else {
    陳述句;
}

條件式運算結果為 true 會執行 if{} 的陳述句,否則執行 else{} 的陳述句,如果條件式不成立時並不想作任何事,else 可以省略。

底下來個運用 if/else 判斷數字為奇數或偶數的範例:

package cc.openhome;

public class Odd {
    public static void main(String[] args) {
        var input = 10;
        var remain = input % 2; 

        if(remain == 1) { // 餘數為1時是奇數
            System.out.printf("%d 是奇數%n", input); 
        } 
        else {
            System.out.printf("%d 是偶數%n", input); 
        }
    }
}

如果 ifelse 中只有一行陳述句,{} 可以省略,不過為了可讀性與可維護性而言,現在建議是就算只有一行陳述句,也要撰寫 {} 明確定義範圍。

Apple曾經提交一個 iOS 的安全更新,原因是在某個函式中有兩個連續的縮排:

if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
        goto fail;
        goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
        goto fail;

因為縮排在同一層,閱讀程式碼時大概也就沒注意到,又沒有 {} 定義區塊,結果就是 goto fail 無論如何都會被執行到的錯誤。

某些人會撰寫所謂的 if/else if 語法:

if(條件式一) {
    ...
}
else if(條件式二) {
    ...
}
else {
    ...
}

其實這是省略 {} 加上程式碼排版後的結果,如果不省略 {},原本的程式應該是:

if(條件式一) {
    ...
}
else {
    if(條件式二) {
        ...
    }
    else {
        ...
    }
}

如果條件式一不滿足,就執行 else 的陳述,而在這邊進行條件式二測試,如果滿足就執行條件式二 {} 的陳述,如果省略了第一個 else{}

if(條件式一) {
    ...
}
else
    if(條件式二) {
        ...
    }
    else {
        ...
    }

Java 是個自由格式語言,可以適當地排列這個片段,就會變為方才看到的 if/else if 寫法,就閱讀上似乎比較好讀一些,例如應用在處理學生的成績等級問題:

package cc.openhome;

public class Level {
    public static void main(String[] args) {
        var score = 88;
        var level = '\0';
 
        if(score >= 90) {
            level = 'A';
        } 
        else if(score >= 80 && score < 90) {
            level = 'B';
        }
        else if(score >= 70 && score < 80) {
            level = 'C';
        }
        else if(score >= 60 && score < 70) {
            level = 'D';
        }        
        else {
            level = 'E';
        }
        System.out.printf("得分等級:%c%n", level);
    }
}

switch

switch 可用於比對整數、字元、Enum、字串,Enum 之後文件會說明。switch 的語法架構如下:

switch(變數或運算式) {
    case 整數字元字串或Enum:
        陳述句;
        break;
    case 整數字元字串或Enum:
        陳述句;
        break;
    ...
    default:
        陳述句;
}

首先看看 switch 的括號,當中置放要取得值的變數或運算式,值必須是整數、字元、字串或 Enum,取得值後會開始與 case 設定的整數、字元、字串或 Enum 比對,如果符合就執行之後的陳述句,直到遇到 break 離開 switch 區塊,如果沒有符合的整數、字元、字串或 Enum,會執行 default 的陳述句,default 不一定需要,如果沒有預設要處理的動作,可以省略 default

來看看方才的 Level 類別,特意改用 switch 實作會如何:

package cc.openhome;

public class Level2 {
    public static void main(String[] args) {
        var score = 88;
        var quotient = score / 10;
        var level = '\0';

        switch(quotient) { 
            case 10: 
            case 9: 
                level = 'A';
                break; 
            case 8: 
                level = 'B';
                break; 
            case 7: 
                level = 'C';
                break; 
            case 6: 
                level = 'D';
                break; 
            default: 
                level = 'E';
        }
        System.out.printf("得分等級:%c%n", level);
    }
}

在這個程式中,使用除法並取得運算後的商數,如果大於 90 的話,除以 10 的商數一定是 9 或 10(100分時),在 case 10 中沒有任何的陳述,也沒有使用 break,繼續往下執行,直到遇到 break 離開 switch,如果比對條件不在 10 到 6 這些值的話,會執行 default 的陳述,這表示商數小於 6,所以學生成績等級就顯示為 E 了。

從 Java SE 14 開始,switch 正式支援運算式形式,就上例來說,可以改為以下的形式:

package cc.openhome;

public class Level3 {
    public static void main(String[] args) {
        var score = 88;
        var quotient = score / 10;
        var level = switch(quotient) { 
            case 10, 9 -> 'A';
            case 8     -> 'B';
            case 7     -> 'C'; 
            case 6     -> 'D'; 
            default    -> 'E';
        };
        System.out.printf("得分等級:%c%n", level);
    }
}

case 的比對上,可以使用逗號區隔來比對多個案例,每個案例的 -> 右方指定值,會成為 switch 的運算值,如此一來,就不用特別如 Level2 的範例,得為 level 設定初始值了。

在需要區塊的情況下,也可以改用 yield 指定 switch 的運算值,例如:

package cc.openhome;

public class Level4 {
    public static void main(String[] args) {
        var score = 88;
        var quotient = score / 10;
        var level = switch(quotient) { 
            case 10, 9:
                yield 'A';
            case 8:
                yield 'B';
            case 7:
                yield 'C'; 
            case 6:
                yield 'D'; 
            default:
                yield 'E';
        };
        System.out.printf("得分等級:%c%n", level);
    }
}

可以看到 switch 作為運算式時,雖然沒有 break,然而執行完案例後,並不會往下個案例繼續執行;必要時,->yield 可以混合使用:

package cc.openhome;

public class Level5 {
    public static void main(String[] args) {
        final String warning = "(喔喔!不及格了!)";
        
        var score = 59;
        var quotient = score / 10;
        var level = switch(quotient) {
            case 10, 9 -> "A";
            case 8     -> "B";
            case 7     -> "C"; 
            case 6     -> "D"; 
            default    -> {
                String message = "E" + warning;
                yield message ;
            }
        };
        System.out.printf("得分等級:%s%n", level);
    }
}

就初學者而言,若要將特定值對應至某些動作或值,switch 是個簡單、便利的工具,然而切記不要濫用,若各個 case 的邏輯或層次變得複雜而難以閱讀時,就應考慮其他設計方式的可行性。

分享到 LinkedIn 分享到 Facebook 分享到 Twitter