Java 開發看的 Scala 入門

前言

對於 Scala 語言其實很早有所耳聞,但沒有真正進一步瞭解,只知道這門語言在大數據領域很火。正如前幾年大數據開發的興起,也着實讓這門基於 JVM 的語言火了一把。因爲近期開始參與公司的大數據項目,面對大數據量計算處理需求,基於目前本身 Java 的技術棧遠遠不夠,不得不引入 Spark 之類的大數據框架,而 Spark 是由 Scala 編寫的,雖然說 Spark 提供 Java API,但爲了能更好了使用 Spark 和及時排查可能遇到的問題,仍是有必要全面學習下 Scala 這門語言。html

本文主要內容涉及以下:java

  • Scala 基本介紹
  • Scala 語法特性介紹
  • Scala 與 SpringBoot 實戰

示例項目:github.com/wrcj12138aa…git

環境支持:程序員

  • JDK 8
  • SpringBoot 2.1.5
  • Maven 3.6.0
  • Scala 2.12.8

認識 Scala

在認識 Scala 以前,咱們先來弄清楚 Scala 的讀法和來歷。尤爲是做爲中國程序員,頭疼的就是面對新技術出現的單詞,本身卻讀不出來或者讀得很變扭,這裏我在百度翻譯中查到了 Scala 的音標 [ˈskɑlə] 和單詞發音,能夠供你們參考。github

接着再簡單看下 Scala 的歷史:Scala 是由洛桑聯邦理工學院的 Martin Odersky 教授主導設計的一門編程語言,面對 Java 嚴格的語言規範限制,Martin 教授基於 JVM 從新設計了一門更加現代化,可擴展的語言, 而且將這門 Scalable Language 的縮寫 Scala 做爲命名,這就是 Scala 的來由。從 2003 年末基於 Java 平臺的 Scala 發佈,到如今穩定版本已經到了 2.12.8,能夠說 Scala 的迭代速度仍是很快的web

好了說到正題,要學習 Scala 這門編程語言,首先來解讀下官方對 Scala 的描述:spring

Scala combines object-oriented and functional programming in one concise, high-level language. Scala's static types help avoid bugs in complex applications, and its JVM and JavaScript runtimes let you build high-performance systems with easy access to huge ecosystems of libraries.sql

從上面這段話裏,咱們能夠提取到如下信息:數據庫

  1. Scala 是一門靜態類型的高級編程語言,結合了面對對象和麪向函數編程的特性。
  2. Scala 能夠運行在 JVM 以及 JavaScript 運行時環境上。
  3. Scala 經常使用於構建高性能程序和框架。

關於 Scala 語言的豐富特性咱們將在下文的 Scala 語法 一節去學習和感覺;而針對構建高性能程序和框架這一點,目前大數據庫領域的項目框架 Spark,Flink,Kafka 等都是基於 Scala 開發的,說明了使用 Scala 對高性能場景下的幫助是毫無疑問的。編程

Scala 的語言特性

  • 既是屬於面向對象的語言,也是函數式語言
  • 靜態類型語言,這一點跟 Java 同樣,但還支持類型推斷
  • 與 Java 生態無縫接合,能夠調用 Java 庫以及與 Java 混合開發
  • 可擴展性,能夠根據自身須要定製擴展語言的功能

總體瞭解 Scala 以後,接下來咱們就開始快速搭建 Scala 環境,而後寫咱們的第一行 Scala 代碼吧。

Scala 環境搭建

搭建 Scala 環境其實很簡單,類比配置 Java 語言環境的經歷,總結下來就兩步驟:

注意:下載 Scala 安裝包以前,須要本機環境有 JDK 8 以上 才行。

  1. Scala 官網下載 2.12.8 版本的語言壓縮包,並解壓到本地磁盤。

    image-20190608011135438

  2. 添加 Scala 環境變量,指定爲所解壓的 Scala 文件夾,因爲我本機爲 Mac 環境,因此只需在 Shell 配置文件裏操做便可,修改 Shell 配置文件 ~/.bash_profile 文件底部添加下面內容, 使用命令source ~/.bash_profile 使配置生效。

    carbon

完成上述步驟後,命令行輸入 scala -version 獲得跟下圖內容同樣時就表示 Scala 語言環境搞定了。

查看 Scala 版本

Scala 命令行交互

Scala REPL

不一樣於 Java,Scala 還提供了命令行模式下的交互,專業說法爲 REPL(Read-eval-print-loop),詳細介紹能夠參見Scala REPL OVERVIEW;基於此咱們能夠直接在命令行上使用 Scala 語言,若是有過 Python 或者 Node.js 的同窗應該會熟悉這個編程交互方式。

首先在命令行裏輸入 scala 進去交互模式,而後輸入 println("Hello,Scala") 回車,這樣一個最簡單的 Scala 版 HelloWorld 就是完成了。

REPL

這裏使用到的 println就是 Scala 中的打印方法,相似 Java 中的System.out.println,是否是很簡潔呢。

編譯 Scala 類

除了使用命令行交互方式以外,咱們再試着實現一個 Scala 類的 HelloWorld。

首先新建一個 HelloWorld.scala 文件, 寫一個 main 方法。

HelloWord.scala

看起來,Scala 的寫法跟 Java 的 main 方法是否是很不同呢,先試着跑起來,咱們後面再對語法進一步學習。

接下來就須要對源代碼進行編譯運行了,相似 Java ,Scala 庫提供了 Scala 編譯器工具 scalac,能夠對 Scala 文件編譯成字節碼;還有解釋器工具 scala 用於運行字節碼。

使用 scalac 進行編譯生成 HelloWorld.class 文件;

scalac HelloWorld.scala
複製代碼

最後若要運行,執行 scala HelloWorld ,控制檯輸入 HelloWorld 就說明運行成功了。

HelloWorld

看過最簡易的兩種 Scala 版 HelloWorld 以後,咱們開始學習一下 Scala 基礎語法和特性吧。

Scala 語法

變量

Scala 聲明變量的關鍵字有兩個:var & valvar 用於可變變量的聲明,是 variable 的簡寫;val 用於常量的聲明,是 value的簡寫。下面是通常的寫法,定義的變量名與類型之間,須要用一個冒號: 隔開。

val x: Int = 1+1
var y: String = "abc"
複製代碼

Scala 中容許不使用分號表示語句結束,換行即認爲新語句的開始

但與 Java 聲明變量時必需要指定變量類型不一樣, Scala 具備類型推斷的特性,就不須要顯示地聲明類型,所以咱們能夠寫成下面形式:

val x = 1+1
var y = "abc"
複製代碼

如今再來講下 varval 的區別:val 用於聲明的常量,當賦值以後就不能再改,嘗試修改值時會編譯錯誤。

carbon

另外,val 變量賦值爲一個引用對象時,能夠改變對象的屬性,但不能再指向其餘對象,因此 val能夠理解爲 Java 中用 final 修飾的變量。

數據類型

在 Scala 中一切都是對象,首先看下官方提供的 Scala 類型層次圖,有跟 Java 相同的數據類型,也有 Scala 特有的類型。

Scala 類型層次圖

Java 中有八大基礎數據,Scala 則有九個基礎數據類型, 除了同樣有 DoubleFloatLongIntShortByteBoolean,還額外多一個 Unit 類型,它表示不帶任何意義的類型,有且僅有一種值:() ,能夠理解爲 Java 中的 void,在函數定義時使用表示該函數無返回值。

這些基礎類型都有一個父類 AnyVal,它表示通用的值類型,與 AnyVal相對的就是引用類型 AnyRef, Scala 中的集合對象(SetMap),字符串類(String) ,以及自定義的類則都屬於 AnyRef,至關於 Java 中的 java.lang.Object

AnyValAnyRef 之上的就是 Scala 最頂級的類型 ,它定義了一些最通用的方法如 equalshashCodetoString

Nothing 和 Null

Scala 類型層次圖底部還有兩個類型 NothingNullNothing是全部類型的子類型,該類型下沒有任何一個值,主要用於程序的異常和中斷的非正常返回;而 Null 爲引用類型的子類型,只有一個值:null ,主要是在與 Java 交互時使用。

值類型轉換

不一樣的值類型,Scala 也支持類型轉換,而且是單向的,下面是值轉換的方向圖。

值類型轉換

而若是嘗試非指定方向的類型轉換,就不會經過編譯,直接提示錯誤: type mismatch,以下示例:

mismatch

方法/函數

在 Scala 中方法和函數一般狀況下能夠認爲是一個東西,只是函數能夠做爲變量單獨使用,能夠理解爲 Java8 的 Lambda 表達式,首先來看下他們如何定義,這一點跟 Java 仍是有很大不一樣的。

方法/函數

上面是函數/方法最簡化的定義方式,針對單行的表達式,Scala 是容許省略大括號和 return 關鍵字的。

首先看下函數的定義,=> 左邊爲參數列表(這裏的類型不能省略),右邊的爲表達式內容,從上面的 add 函數變量類型輸出能夠看出 add 函數變量類型爲 (Int, Int) => Int,底層是一個 Lambda 相關的對象。

匿名函數,也叫作閉包:表示沒有名字的函數,將定義的函數沒有指定變量名稱直接使用,如 (x: Int) => x + 1

Scala 定義方法須要使用 def 關鍵字,這是 Java 所沒有的,而且 def 後面緊跟方法名稱,參數列表,返回類型和方法體,下面給出常見的方法定義語法

定義方法

須要注意的是,在 Scala 方法中默認將最後一行的語句結果做爲返回值 。(跟 Java 同樣,Scala 也有 return 關鍵字,但爲了簡潔不多使用)。而若是方法定義返回值類型爲 Unit時,則表示該方法沒有返回值。

return

除此以外,若是調用的函數或者方法沒有入參,能夠把括號省略。

接下 Scala 函數/方法中所支持的一些特性:可變參數,默認參數,命名參數:

1.可變參數

Scala 容許指明函數的最後一個參數是能夠重複的,在參數類型後面放星號 *,表示可重複的參數,Java 中使用 ...

可變參數

一樣函數內部用一個數組接收可變參數。

2.默認參數

Scala 容許爲函數參數指定默認值,這樣即便調用函數過程當中不傳遞參數,函數就是採用它的默認參數值,若是傳了參數,默認值就會被忽略,這一特性是 Java 所沒有的,但使用起來也很方便簡單。

默認參數

3.命名參數

儘管函數定義時指定了參數列表順序,可是 Scala 支持不按照定義順序,按照指定參數名字進行參數傳遞,實例以下:

命名參數

流程控制

程序中一般涉及的流程控制主要兩種:條件判斷和循環處理。

條件判斷

條件判斷上,Scala 有與 Java 同樣的 if-else 語法,除此以外,Scala 提供了模式匹配的機制進行條件判斷。

它是 Java 的 switch 語句的升級版,首先看下語法:

模式匹配

這是使用新關鍵字 match , 左側爲被匹配的值,而右側包含了四個 case 表達式,每一個表示一種條件,最後的 case _ 表示匹配其他全部狀況,相似 switch-case 語法的 default 做用。

match 表達式容許接受返回值,正如上方式示例會返回 String 類型,因此模式匹配一般定義在函數中,以下:

模式匹配2

若是匹配的條件裏要執行多個語句,則就須要使用大括號 {}包含了,須要注意的是模式匹配一旦某個條件匹配成功,就不會執行其餘條件的語句,自帶了 break 的效果。

Scala 的模式匹配在本文僅進行簡單的用法演示,還有豐富的用法詳細可見官方文檔-模式匹配一節

循環處理

Scala 循環語法比較特別,使用 for + <- 來進行遍歷元素,並提供了便捷的 until方法,to方法來實現遍歷, 須要注意的就是 until方法採用半閉區間,不包含索引最後一位。而 until方法,to方法底層都是構建 Range 對象而後進行遍歷。

Scala  循環

除了上面簡單用法以外,在 for 循環中還能夠配合 if 條件進行遍歷過濾,以下面簡單的示例,是否是很靈活呢。

for

Scala 中類的概念與 Java 的基本一致,而語法形式和用法上更加豐富靈活。

先看下類的定義,沒看錯下面是 Scala 最簡單的類的定義和使用了,建立了一個 User 對象,賦值給了 user1 變量。

類的定義和使用

new 關鍵字用於建立實例,因爲當前 User 沒有定義任何構造器,所以只有一個默認的無參構造器,能夠直接省略括號()

構造器

再看下一個帶有自定義構造器的類如何定義,看起來是否是很奇怪,緊跟類名括號後面的就是構造器所需的參數。

構造器

這裏會有個疑問:若是有多個構造器函數,那這個類都該如何定義呢?來看下示例寫法,基於原有構造器,用 def this(...)定義了一個新的方法,內部執行 this(name,age) 實際調用了原來的構造器。Scala 將這個定義方法稱爲輔助構造器,緊跟類名後的爲主構造器。

多個構造器

Scala 容許一個類有多個輔助構造器,但只能有一個主構造器,而且輔助構造器內部第一行必須調用主構造器,這裏是能夠經過多個輔助構造器間接地調用主構造器。

若是輔助構造器有額外的構造參數,則須要添加字段關聯,不會像主構造器同樣自動生成字段如上面 Person 類的 nameage

成員變量和 Getter/Setter 語法

在 Java 開發中,一般咱們會將 Java Bean 的成員變量私有化,而後提供 Getter/Setter 方法供外部操做該變量,那邊在 Scala 類中有該如何操做呢,其實也很簡單,首先看下示例。成員變量的成員默認是public的,可使用private訪問修飾符能夠在函數外部隱藏它們。

成員變量

注意這邊字段聲明時使用了 _ 關鍵字,這個在 Scala 中表示特定類型的默認值,用於佔位的做用,若是對於爲 String 類型,則該值就是空字符串。

繼承重寫

繼承是面嚮對象語言的重要的特性之一,Scala 在繼承方面也跟 Java 同樣,使用 extends ,而且只容許單繼承。繼承狀況有兩種,一種爲父類採用了默認構造器,另一種就是父類有自定義的構造器,子類定義時必須知足父類構造器參數的要求,先會執行父類的主構造器,在執行子類本身的構造器。

繼承

當子類繼承父類後,想要重寫父類方法也十分簡單,使用 override 關鍵字修飾須要重寫的訪問,子類默認執行父類方法例如這樣的形式 super.foo(),咱們只需調整爲自定義的實現便可。

Trait

Trait 翻譯過來就是特徵,特性的意思,看似比較新穎,其實它就相似於 Java 的接口,用於擴展咱們的類。跟 Java 的 Interface 同樣,Trait 也沒法被實例化,一般提供若干個抽象方法和字段,由具體的類使用 extends 關鍵字實現。

有一點不一樣的是,當類須要實現多個 Trait 時,extends 只能指向一個 Trait,其他的 Trait 都須要使用 with 關鍵字 關聯,以下方給出的示例。

Trait

Case Class

Case Class 是 Scala 特有的一種特殊的類,定義方式雖然跟普通類同樣,但須要使用 case class 組合關鍵字修飾。主要用於不可變的數據和模式匹配中。

Case Class

實例化 Case Class 類時不須要使用 new 關鍵字,而是有一個默認的apply方法來負責對象的建立。而且賦值字段都是默認用public val修飾,讓字段不可更改。

額外注意的是當兩個 Case Class 對象進行 == 比較的時候,Scala 是按照值比較,而不是引用比較。

單例對象

Scala 將單例對象單獨用 object 關鍵字單獨聲明出來,定義方式用類同樣, 如 object Box。但單例對象也是屬於一種特殊的類,有且只有一個實例,而且當它第一次被使用時纔會建立。

伴生對象與伴生類

Scala 容許一個單例對象與某個類共用一個名稱,而這樣二者造成了伴生的關係,單例對象成爲該類的伴生對象,而類成爲單例對象的伴生類。伴生的關係主要體如今於,伴生對象裏定義的成員,能夠在伴生類上使用,這一點就是比如在 Java 中 static 成員。

carbon

Tuple 元組

元組也屬於 Scala 中一個特殊的類,它容許包含不一樣類型的元素,而且爲不可變。常見的用法就是當咱們須要函數返回多個值時,這點就可強大了。

首先看下元組的建立和使用

Tuple

Tuple2 類屬於 Scala 定義的元素類,從 Tuple2...Tuple22 一共有 21 個這樣類,每一個類表示當前所對應的參數個數,也就是元組中最多包含 22 個參數。

元組訪問起來也很方便,用tuple._n方式去取第 n 個元素。

集合

除了兼容 Java 的集合框架,Scala 還提供了豐富強大的集合類,咱們主要來學習下 Scala 中最經常使用的幾種集合。

Set

一種不包含重複元素的容器對象,分爲不可變 Set 和可變 Set 兩種,默認爲不可變。

Set

不可變 Set 經常使用的操做方法有

  • xs.contains(x)xs(x) 判斷集合是否存在該元素
  • xs intersect ysxs & ys 兩個集合取交集
  • xs union ysxs | ys 兩個集合取並集
  • xs diff ysxs &~ ys 兩個集合取差集

可變 Set 經常使用的操做方法有

  • xs add x 添加到集合
  • xs remove x 從集合中移除
  • xs.clear() 清空集合
  • xs(x) = bxs.update(x, b) 將集合更新

Map

Map 集合特色就是以鍵值對結構存儲。使用語法上有兩種:

  • key -> value 方式添加元素
  • (key, value) 方式添加元素

Map 集合與 Set 集合相似,也有不可變和可變之分,默認爲不可變。

Map

經常使用的 Map 操做有

  • map.keys 返回含有全部 key 的可迭代對象
  • map.keySet返回一個含全部 key 的集合
  • map.values 返回一個含全部 value 的可迭代對象
  • map(k) = v 更新元素
  • map.put(k, v) 新增元素
  • map.remove(k) 移除元素
  • map.clear()清空 Map 集合

數組

Scala 數組與 Java 數組是一一對應的。一樣把 Array 做爲數組的標識符,下面先看下如何聲明一個定長數組和簡單使用。

定長數組

訪問數組指定元素時採用括號+索引的形式,當數組元素沒有值時,用 null 表示。

Scala 除了定長數組以外,還提供了可變數組的實現 ArrayBuffers,這個 Scala 提供的可變容器的一種數組實現,能直接訪問和修改底層數組,具體使用方式以下:

Array Buffers

Scala 提供了許多可變容器集合,如 ArrayBuffer,ListBuffer,鏈表,隊列等等,都封裝在 scala.collection.mutable.* 包下。

使用數組就會有越界訪問的狀況,當 Scala 數組出現越界訪問時,拋出 java.lang.IndexOutOfBoundsException異常。

越界訪問

Scala 實戰:Scala + Spring Boot

接觸了那麼多 Scala 語法和特性以後,咱們經過用 Scala 語言來編寫簡單的 Spring Boot Web 應用訪問 hello 請求,來看下它是如何跟 Java 混合開發的。

咱們使用 IDEA 做爲開發工具,要使用 Scala 語言,爲了讓 IDE 提供更好的 Scala 語言支持,能夠先安裝 Scala 插件

Scala 插件

咱們能夠經過 start.spring.io/ 建立一個普通的 SpringBoot 項目,下載到本地後解壓用 IDEA 打開。

基於原來的目錄接口,新建一個 scala 源代碼目錄。

scala 源代碼目錄

Scala 的源代碼目錄建立後,轉到 Pom 文件,修改 XML 配置:

  1. 首先引入 Scala 語言庫, 這裏版本爲 2.12.8

    Scala 語言庫

  2. 加入 Scala 依賴包以後,咱們還須要添加一個用於編譯 Scala 的 Maven Plguin。

    Maven Plguin

好了,到此 Scala 的項目配置已經完成,接下來就能夠編寫 Scala 代碼了。

爲了接受 hello 請求,項目 POM 先別忘了依賴 spring-boot-starter-web

新建一個 Scala 類 HelloContrroller.scala,實現一個接受 /hello 請求的方法,以下

HelloContrroller.scala

而後直接啓動項目引導類 com.blog4one.learn.ScalaActionsApplication,啓動日誌以下則表示啓動成功,默認服務監聽 8080 端口。

啓動日誌

打開瀏覽器,輸入 http://localhost:8080/hello,回車便可看到結果。

image-20190608234156015

瀏覽器成功的響應,也說明了 Scala 編寫的 HelloContrroller.scala 能接受並處理響應,而底層仍然是基於 Java 框架 Spring Boot 構建。

結語

好了,基本的 Scala 語法和特性先介紹到這裏,經過此次學習想必也看到了 Scala 雖然是基於 JVM 上運行,可是語法和功能都極其靈活,想要掌握還須要花必定精力時間, 後續繼續對 Scala 高級特性和用法進行介紹,感興趣的小夥伴能夠關注個人微信公衆號,會在第一時間更新哈。

每篇一句

參考

相關文章
相關標籤/搜索