嘗鮮Java 12新特性:switch表達式

来源:https://www.cnblogs.com/tjxing/archive/2019/01/22/10306772.html
-Advertisement-
Play Games

Java 12將在兩個月後(2019/3/19)發佈,現已進入RDP1階段,確定加入8個JEP。其中對Java語法的改進是JEP 325: switch表達式。於是我迫不及待,提前感受一下更先進的語言特性。 ...


Java 12將在兩個月後(2019/3/19)發佈,現已進入RDP1階段,確定加入8個JEP。其中對Java語法的改進是JEP 325: switch表達式。於是我迫不及待,提前感受一下更先進的語言特性。

因為12沒有正式發佈,本文使用自己編譯的OpenJDK。嫌麻煩的話,也可以直接使用官方的ea版本。JEP325是預覽(preview)特性,編譯運行時需要添加--enable-preview參數。

顧名思義,這個feature是對switch動手腳的。包括兩個方面。

1. 簡化fall-through規則

下麵這樣的switch代碼我們寫過幾萬遍了

switch (today) {
    case SATURDAY:
    case SUNDAY:
        System.out.println("I'm happy!");
        break;
    case MONDAY:
    case TUESDAY:
    case WEDNESDAY:
    case THURSDAY:
    case FRIDAY:
        System.out.println("I'm sad...");
        break;
    default:
        System.out.println("I'm confused.");
}

這段代碼存在的問題是:

1. 內容不符合愛崗敬業的核心價值觀(敲黑板!重要!!)
2. 多個條件對應相同代碼時(比如MONDAY到FRIDAY),要重覆寫多個case,冗餘且醜陋
3. 每一段代碼後面都要有break,一旦忘記就會有編譯器檢測不到的邏輯錯誤
4. 變數作用域混亂

第四個問題可能長被忽略。case或者default後面是一連串的語句,而不是代碼塊(註意,它是沒有大括弧的)。這種情況下定義的局部變數,其作用域不是case後的部分,而是整個switch結構。因此,下麵的代碼無法通過編譯。

switch (today) {
    case MODAY:
        int x = 1;
        break;
    default:
        int x = 0; //Variable x is already defined in the scope
}

編譯器看到的是在一個作用域中存在兩個x,非常違背人類的直覺。

上面的四個問題,除了1,剩下的萬惡之源就是fall-through規則。即switch結構在找到第一個匹配的case條件後,會順序執行後面所有case對應的代碼,無論是否判斷為真。這是40多年前C語言創造後來Java原樣照抄的經典語法,但在今天看起來就顯得很呆萌了,新的語言也幾乎都放棄了fall-through。

好在,儘管後知後覺,從12開始Java開發者也可以選擇更簡潔清晰的語法了。就像這樣

switch (today) {
    case SUNDAY, SATURDAY -> System.out.println("I'm happy!");
    case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> System.out.println("I'm happy, too!!");
    default -> System.out.println("I'm confused.");
}

很容易看出語法的變化,這些變化也解決了上面的四個問題。歸納一下:

1. 程式內容積極向上,體現了新時代的奮鬥精神(敲黑板!重要!!)
2. 對應相同動作的多個case合併為一行,代碼更簡潔
3. 條件和動作之間用->連接,這時fall-through規則失效。匹配到的分支代碼執行完後直接跳出,不會繼續執行下麵的case對應的代碼。也就是不需要再為每一個分支寫break了。程式更簡潔清晰,也更符合人類的直覺。
   需要註意,為了保持向後相容性,case條件後依然可以使用:,這時fall-through還是有效的,即不能省略原有的break。而一個switch結構里不能混用->:,否則會有編譯錯誤。
4. 每一個->後面只允許接一個表達式、一個代碼塊、或者一個throw語句。這樣在代碼塊中定義的局部變數,其作用域就限制在代碼塊中,而不是蔓延到整個switch結構。邏輯更加清楚了。

2. switch作為表達式(expression)

switch結構一直是一個statement,而從Java 12開始,它也可以用作expression。從學院派的定義理解statement和expression的區別叫人頭疼,如果說人話的話,就是switch可以有返回值了。

作為statement的switch沒有返回值,所以我們不能寫出這樣的代碼

x = switch (y) { ... }

如果需要根據不同的條件給某個變數賦值,我們以前只能這樣做

String word = "";
switch (num) { case 1: word = "One"; break; case 2: word = "Two"; break; default: String result = String.format("Other (%d)", num); word = result; }

讓人難受的地方有兩個。

1. 重覆多次地寫賦值語句,繁瑣且易錯。
2. 這段程式的終極目標是為word變數賦值,而賦值前必須在其他的地方初始化word,淡化了二者的邏輯關係,代碼也顯得瑣碎。

從12開始我們可以這樣改造代碼

String word = switch (num) {
    case 1 -> "One";
    case 2 -> "Two";
    default -> {
        String result = String.format("Other (%d)", num);
        break result;
    }
};

可見,switch成了一個表達式(expression),它有自己的返回值。每一個分支只需要決定具體的返回值是什麼,不需要考慮如何使用這個值。而全程只需要一次賦值操作。代碼整體變得更簡潔、緊湊、清晰。

而返回值又有兩種寫法。還記得嗎,上一節提到過,->後只能接三樣東西:表達式、代碼塊、throw語句。throw的情況沒有返回值,先不管它。另外兩種情況:

1. 如果分支只有一個表達式,那麼表達式本身就是switch的值,比如上面例子里的"One""Two"
2. 如果分支是一個代碼塊,比如例子中的default,可以看到Java 12改造了break關鍵字,可以通過break result的形式返回值。switch並沒有拋棄break,而是賦予它更重要的職能。

作為expression的switch也可以使用:,在這種情況下,各個分支必須用break關鍵字返回值。像這樣

String word = switch (num) {
    case 1 : break "One";
    case 2 : break "Two";
    default : {
        String result = String.format("Other (%d)", num);
        break result;
    }
};

上面例子中,case 1case 2中的break不能省略,否則會有編譯錯誤。

很顯然,當switch用作expression時,每一個分支都必須有返回值(或者有throw異常)。我們不能寫下麵這樣的代碼

String word = switch (num) {
    case 1 -> "One";
    case 2 -> "Two";
    default -> {
        System.out.println("莫挨老子");
        //錯誤: switch rule completes without providing a value
    }
};

編譯器不知道當num=3的時候應該返回什麼,於是它憤怒地拋出了一個錯誤。

最後要強調,switch在不返回值的時候,還是一個statement。而作為expression並且在一句代碼的結尾處時,不要忘了後面的分號!(親自踩坑,友情提醒)

To be continue...

可能你會覺得這些改進還是小修小改,不值得過分激動。但是,JEP 325是JEP 305: Pattern Matching的依賴。雖然沒有最終確定,但或許Pattern Matching會在不久後的幾個版本正式引入,到時又將是語言層面的大革命。後續的幾個版本還是值得期待的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 作為普通的網民來說,一般不需要知道也不用關心什麼是盜鏈,不過如果你是網站的開發者或維護者,就不得不重視盜鏈的問題了。如果你剛剛開發完一個沒有防盜鏈的帶有文件下載功能的網站,掛上internet,然後上傳幾個時下非常熱門的軟體或電影併在網站內公佈下載地址,讓MSN上的所有好友都來體驗一下你的傑作。 不 ...
  • 參考:https://www.cnblogs.com/lewis0077/p/5133812.html(深入解析策略模式) 參考:https://www.cnblogs.com/lewis0077/p/5133812.html(深入解析策略模式) 定義: 策略模式定義了一系列的演算法,並將每一個演算法封 ...
  • 前言 在上一篇中我們學習了結構型模式的組合模式和過濾器模式。本篇則來學習下結構型模式最後的兩個模式, 享元模式和代理模式。 享元模式 簡介 享元模式主要用於減少創建對象的數量,以減少記憶體占用和提高性能。這種類型的設計模式屬於結構型模式,它提供了減少對象數量從而改善應用所需的對象結構的方式。 用通俗的 ...
  • 基本語法 1.Grovvy的註釋分為//和/**/和java的一樣. 2.Grovvy語法可以不已分號結尾. 3.單引號,裡面的內容嚴格的對應java中的String,不對$符號進行轉義. 4.雙引號“ ”的內容中如果有$號的話,會先對錶達式先求值. 5.三個引號中的字元串支持隨意的換行. 定義變數 ...
  • 1. 手機APP數據 寫在前面 繼續練習pyspider的使用,最近搜索了一些這個框架的一些使用技巧,發現文檔竟然挺難理解的,不過使用起來暫時沒有障礙,估摸著,要在寫個5篇左右關於這個框架的教程。今天教程中增加了圖片的處理,你可以重點學習一下。 2. 手機APP數據 頁面分析 咱要爬取的網站是 這個 ...
  • Java反射,註解,以及動態代理 基礎 最近在準備實習面試,被學長問到了Java反射,註解和動態代理的內容,發現有點自己有點懵,這幾天查了很多資料,就來說下自己的理解吧【如有錯誤,望指正】 Java反射 首先,我們得弄清一個,什麼是反射(Reflection)。簡單的來說,反射就是讓我們在程式運行的 ...
  • 實現跳轉的方法: 1.Php中header的函數 2js中location函數 3.Html中的meta函數 引入message.html <meta http-equiv="Refresh" content ="<?php echo $wait;?> ;url=<?php echo $url;?> ...
  • 1.簡介 HashMap是基於哈希表的Map介面的實現,用來存放鍵值對(Entry<Key,Value>),並提供可選的映射操作。使用put(Key,Value)存儲對象到HashMap中,使用get(Key)從hashMap中獲取對象。 2.底層結構 HashMap的底層是由數組加鏈表實現的,因為 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...