我去,你寫的 switch 語句也太老土了吧

昨天早上經過遠程的方式 review 了兩名新來同事的代碼,大部分代碼都寫得很漂亮,嚴謹的同時註釋也很到位,這令我很是滿意。但當我看到他們當中有一我的寫的 switch 語句時,仍是忍不住破口大罵:「我擦,小王,你丫寫的 switch 語句也太老土了吧!」java

來看看小王寫的代碼吧,看完不要罵我裝逼啊。express

private static String createPlayer(PlayerTypes playerType) {
    switch (playerType) {
        case TENNIS:
            return "網球運動員費德勒";
        case FOOTBALL:
            return "足球運動員C羅";
        case BASKETBALL:
            return "籃球運動員詹姆斯";
        case UNKNOWN:
            throw new IllegalArgumentException("未知");
        default:
            throw new IllegalArgumentException(
                    "運動員類型: " + playerType);

    }
}
複製代碼

看完上述代碼後,你是否是會發出這樣的感慨——「代碼寫得很好,沒有任何問題啊!」是否是以爲我在無事生非,錯怪了小王!但此時我要送上《了不得的蓋茨比》中的一句話:spa

我年紀還輕,閱歷不深的時候,我父親教導過我一句話,我至今還念念不忘。 「每逢你想要批評任何人的時候, 」他對我說,「你就記住,這個世界上全部的人,並非個個都有過你擁有的那些優越條件。」3d

哈哈,這句話不光是讓你看的,也是給我看的。是時候冷靜下來談談上述 switch 語句的老土問題了。code

看到上圖了吧,當不當心刪掉 default 語句後,編譯器就會報錯,提示:「沒有返回語句」,爲了解決這個問題,咱們能夠新建一個 player 變量做爲返回結果,就像下面這樣:cdn

private static String createPlayer(PlayerTypes playerType) {
    String player = null;
    switch (playerType) {
        case TENNIS:
            player = "網球運動員費德勒";
            break;
        case FOOTBALL:
            player = "足球運動員C羅";
            break;
        case BASKETBALL:
            player = "籃球運動員詹姆斯";
            break;
        case UNKNOWN:
            throw new IllegalArgumentException("未知");
    }

    return player;
}
複製代碼

當添加了 player 變量後,case 語句中就須要添加上 break 關鍵字;另外在 switch 語句結束後,返回 player。這時候,編譯器並不會提示任何錯誤,說明 default 語句在這種狀況下是能夠省略的。blog

從 JDK 12 開始(本例使用的是 JDK 13),switch 語句升級了,不只能夠像傳統的 switch 語句那樣做爲條件的判斷,還能夠直接做爲一個返回結果。來對小王的代碼進行改造,以下所示:ci

private static String createPlayer(PlayerTypes playerType) {
   return switch (playerType) {
        case TENNIS -> "網球運動員費德勒";
        case FOOTBALL -> "足球運動員C羅";
        case BASKETBALL -> "籃球運動員詹姆斯";
        case UNKNOWN ->  throw new IllegalArgumentException("未知");
    };
}
複製代碼

夠 fashion 吧?不只 switch 關鍵字以前加了 return 關鍵字,case 中還見到了 Lambda 表達式的影子,中劃線和箭頭替代了冒號,意味着箭頭右側的代碼只管執行無須 break。get

而且,default 語句變成了可選項,無關緊要,不信?你也動手試試。編譯器

新的 switch 語句足夠的智能化,除了有上述的 3 個優點,還能夠對枚舉類型的條件進行校驗。假如在 PlayerTypes 中增長了新的類型 PINGPANG(乒乓球):

public enum PlayerTypes {
    TENNIS,
    FOOTBALL,
    BASKETBALL,
    PINGPANG,
    UNKNOWN
}
複製代碼

此時編譯器會發出如下警告:

意思就是 switch 中的 case 條件沒有徹底覆蓋枚舉中可能存在的值。好吧,那就把 PINGPANG 的條件加上吧。來看一下完整的代碼:

public class OldSwitchDemo {
    public enum PlayerTypes {
        TENNIS,
        FOOTBALL,
        BASKETBALL,
        PINGPANG,
        UNKNOWN
    }

    public static void main(String[] args) {
        System.out.println(createPlayer(PlayerTypes.BASKETBALL));
    }

    private static String createPlayer(PlayerTypes playerType) {
        return switch (playerType) {
            case TENNIS -> "網球運動員費德勒";
            case FOOTBALL -> "足球運動員C羅";
            case BASKETBALL -> "籃球運動員詹姆斯";
            case PINGPANG -> "乒乓球運動員馬龍";
            case UNKNOWN -> throw new IllegalArgumentException("未知");
        };
    }
}
複製代碼

switch 語句變成了強大的 switch 表達式,美滋滋啊!那假如一個運動員既會打籃球又會打乒乓球呢?

private static String createPlayer(PlayerTypes playerType) {
    return switch (playerType) {
        case TENNIS -> "網球運動員費德勒";
        case FOOTBALL -> "足球運動員C羅";
        case BASKETBALL,PINGPANG -> "牛逼運動員沉默王二";
        case UNKNOWN -> throw new IllegalArgumentException("未知");
    };
}
複製代碼

就像上述代碼那樣,使用英文逗號「,」把條件分割開就好了,666 啊!

不服氣?switch 表達式還有更厲害的,-> 右側還能夠是 {} 括起來的代碼塊,就像 Lambda 表達式那樣。

private static String createPlayer(PlayerTypes playerType) {
    return switch (playerType) {
        case TENNIS -> {
            System.out.println("網球");
            yield "網球運動員費德勒";
        }
        case FOOTBALL -> {
            System.out.println("足球");
            yield "足球運動員C羅";
        }
        case BASKETBALL -> {
            System.out.println("籃球");
            yield "籃球運動員詹姆斯";
        }
        case PINGPANG -> {
            System.out.println("乒乓球");
            yield "乒乓球運動員馬龍";
        }
        case UNKNOWN -> throw new IllegalArgumentException("未知");
    };
}
複製代碼

細心的同窗會發現一個以前素未謀面的關鍵字 yield,它和傳統的 return、break 有什麼區別呢?

先來看官方的解釋:

A yield statement transfers control by causing an enclosing switch expression to produce a specified value.

意思就是說 yield 語句經過使一個封閉的 switch 表達式產生一個指定值來轉移控制權。爲了進一步地瞭解 yield 關鍵字,咱們能夠反編譯一下字節碼:

private static String createPlayer(NewSwitchDemo3.PlayerTypes playerType) {
    String var10000;
    switch(playerType) {
        case TENNIS:
            System.out.println("網球");
            var10000 = "網球運動員費德勒";
            break;
        case FOOTBALL:
            System.out.println("足球");
            var10000 = "足球運動員C羅";
            break;
        case BASKETBALL:
            System.out.println("籃球");
            var10000 = "籃球運動員詹姆斯";
            break;
        case PINGPANG:
            System.out.println("乒乓球");
            var10000 = "乒乓球運動員馬龍";
            break;
        case UNKNOWN:
            throw new IllegalArgumentException("未知");
        default:
            throw new IncompatibleClassChangeError();
    }

    return var10000;
}
複製代碼

編譯器在生成字節碼的時候對 yield 關鍵字作了自動化轉義,轉成了傳統的 break 語句。這下清楚了吧?

可是,話又說出來,那些看似 fashion 的代碼也不過是把部分秀技的工做交給了編譯器,還可能存在對舊版本不兼容、對隊友不友好的問題——代碼土點就土點唄,沒準是最實用的。

「很差意思,我爲昨天早上的囂張向你道歉。。。。。。」我向小王發送了一條信息。

好了,我親愛的讀者朋友,以上就是本文的所有內容了,但願可以博得你的歡心。對了,我還有一個小小的請求:原創不易,若是以爲有點用的話,請不要吝嗇你手中點贊的權力——由於這將是我寫做的最強動力。

相關文章
相關標籤/搜索