柚子快報(bào)邀請(qǐng)碼778899分享:Scala語(yǔ)言的特性
柚子快報(bào)邀請(qǐng)碼778899分享:Scala語(yǔ)言的特性
由于Spark是由Scala編寫(xiě)的,為了使用Spark, 需要掌握Scala這門語(yǔ)言。由于Scala與Java有很多相似的地方,所以在Java的基礎(chǔ)上注意其特性就能很快熟悉Scala。
Scala是一門以Java虛擬機(jī)(JVM)為運(yùn)行環(huán)境并將面向?qū)ο蠛秃瘮?shù)式編程的最佳特性結(jié)合在一起的靜態(tài)類型編程語(yǔ)言,它具有以下特點(diǎn):
Scala是一門多范式的編程語(yǔ)言,Scala支持面向?qū)ο蠛秃瘮?shù)式編程。多范式,就是多種編程方法的意思。有面向過(guò)程、面向?qū)ο?、泛型、函?shù)式四種程序設(shè)計(jì)方法。Scala在設(shè)計(jì)時(shí)參考了Java的設(shè)計(jì)思想,將函數(shù)式編程語(yǔ)言的特點(diǎn)融合到JAVA中。Scala源代碼(.scala)會(huì)被編譯成Java字節(jié)碼(.class),然后運(yùn)行于JVM之上,并可以調(diào)用現(xiàn)有的Java類庫(kù),實(shí)現(xiàn)兩種語(yǔ)言的無(wú)縫對(duì)接。Scala單作為一門語(yǔ)言來(lái)看,非常的簡(jiǎn)潔高效。
1 安裝配置
1.1 環(huán)境配置
從Scala官網(wǎng)下載對(duì)應(yīng)版本的壓縮包:https://www.scala-lang.org/download/
解壓到指定目錄后配置環(huán)境變量
之后在命令行輸入scala啟動(dòng)Scala環(huán)境成功
1.2 IDEA創(chuàng)建Scala項(xiàng)目
在IDEA中安裝Scala插件:
點(diǎn)擊 File->Setting->點(diǎn)擊 Plugins->在搜索框輸入 Scala->點(diǎn)擊 Install->點(diǎn)擊 ok->點(diǎn)擊 apply,重啟IDEA后生效
為項(xiàng)目引入Scala框架支持:
在項(xiàng)目文件夾上點(diǎn)擊右鍵-> Add Framework Support… ->選擇 Scala后點(diǎn)擊確定。如果第一次使用需要點(diǎn)擊創(chuàng)建按鈕,引入scala庫(kù)后才會(huì)顯示scala-sdk庫(kù) 3. 創(chuàng)建scala源文件目錄 先創(chuàng)建普通目錄,右鍵點(diǎn)擊 main 目錄->New->點(diǎn)擊 Diretory ->命名為scala 將該目錄標(biāo)記為源文件目錄,右鍵點(diǎn)擊 scala 目錄->Mark Directory as->選擇 Sources root 4. 創(chuàng)建類 右鍵點(diǎn)擊 scala 目錄->New->Scala Class->Kind 項(xiàng)選擇 Object->Name 項(xiàng)輸入類名HelloScala,就生成了一個(gè)scala對(duì)象。輸入以下代碼運(yùn)行控制臺(tái)輸出"hello scala"
object HelloScala {
def main(args: Array[String]): Unit = {
println("hello scala")
}
}
查看HelloScala生成的target文件夾可以看到Object編譯后生成HelloScala$.class和HelloScala.class兩個(gè)文件
HelloScala類中有一個(gè)main()函數(shù),其中調(diào)用HelloScala$的靜態(tài)對(duì)象MODULE$
public final class HelloScala (
public static void main(String[] paramArrayOfString) (
Hello$.MODULE$.main(paramArrayOfString);
}
}
HelloScala$.class文件中通過(guò)靜態(tài)對(duì)象調(diào)用HelloScala$的main()函數(shù),Scala是一個(gè)完全面向?qū)ο蟮恼Z(yǔ)言,所以沒(méi)有靜態(tài)語(yǔ)法,而是通過(guò)伴生對(duì)象單例的方式來(lái)調(diào)用靜態(tài)方法
import scala.Predef.;
public final class HelloScala$ {
public static HelloScala$ MODULE$;
static {
new HelloScala$();
}
public void main(final String[] args) {
.MODULE$.println("hello scala");
}
private HelloScala$() {
MODULE$ = this;
}
}
2 變量
通過(guò)官網(wǎng)可以查看Scala的API:https://www.scala-lang.org/api/2.12.15/scala/index.html
2.1 注釋
Scala 注釋使用和 Java 完全一樣。
//單行注釋
/* 多行注釋 */
/**
* 文檔注釋
*/
2.2 輸入輸出
通過(guò)StdIn讀取鍵盤的標(biāo)準(zhǔn)輸入
// 讀取一行字符串
var name = StdIn.readLine()
// 讀取short
var age = StdIn.readShort()
// 讀取double類型
var sal = StdIn.readDouble()
Scala的輸出除了和Java類似使用println()按行輸出和printf()格式化輸出外,還可以使用三個(gè)雙引號(hào)進(jìn)行多行字符串輸出,其中使用|進(jìn)行拼接
val s =
"""
|select
| name,
| age
|from user
|where name="zhangsan"
""".stripMargin
println(s)
還可以直接在字符串中通過(guò)$引用變量,在${}內(nèi)進(jìn)行變量運(yùn)算,注意要在字符串前面加s
var age=22
println(s"今年我 $age 歲,明年我${age+1}歲")
//輸出:今年我 22 歲,明年我23歲
2.3 變量
Scala用關(guān)鍵字var來(lái)聲明變量,val聲明常量,常量聲明后不可更改,具有以下幾個(gè)特點(diǎn)
類型推導(dǎo):聲明變量時(shí),類型可以省略,編譯器自動(dòng)推導(dǎo)強(qiáng)數(shù)據(jù)類型:類型確定后,就不能修改變量聲明時(shí),必須要有初始值
//var 變量名 [: 變量類型] = 初始值
var i:Int = 10
//val 常量名 [: 常量類型] = 初始值
val j:Int = 20
雖然val對(duì)象本身不變,但是在使用時(shí),其指向的內(nèi)容卻可以改變
class Person{
var name : String = "jinlian"
}
object TestVar {
def main(args: Array[String]): Unit = {
// p1 是 var 修飾的,p1 的屬性可以變,而且 p1 本身也可以變
var p1 = new Person()
p1.name = "dalang"
p1 = null
// p2 是 val 修飾的,那么 p2 本身不可變,它固定地指向一個(gè)內(nèi)存地址
val p2 = new Person()
// p2 = null // 錯(cuò)誤的,因?yàn)?p2 是 val 修飾的
p2.name="jinlian" //但是p2指向的內(nèi)容可以變,內(nèi)存地址中的內(nèi)容可變
}
}
Scala 中的標(biāo)識(shí)符聲明,基本和 Java 一樣以字母或者下劃線開(kāi)頭,后接字母、數(shù)字、下劃線,但是細(xì)節(jié)上會(huì)有所變化。
// 若以操作符開(kāi)頭,則只能包含操作符
var +*-/#! : String = "" // ok
var +*-/#!1 : String = "" // 錯(cuò)誤
//用反引號(hào)`....`包括的任意字符串,即使是關(guān)鍵字
var if : String = "" // error 不能用關(guān)鍵字
var `if` : String = "" // ok 用反引號(hào)包裹
2.4 數(shù)據(jù)類型
Java有基本類型char、int、double等,為了便于使用分別對(duì)其進(jìn)行了包裝產(chǎn)生了包裝類Character、Integer和Double等,所以Java語(yǔ)言并不是真正的面向?qū)ο?。Scala中的一切數(shù)據(jù)都是對(duì)象,基本數(shù)據(jù)類型如下所示:
Scala中一切數(shù)據(jù)都是Any的子類,整體上分為兩大類:數(shù)值類型AnyVal和引用類型AnyRef。其中數(shù)值類型中的Byte、Short、Int、Long、Float、Double低精度的類型可以向高精度類型進(jìn)行自動(dòng)轉(zhuǎn)換。StringOps用于字符串處理,可以看作是Java的String類的增強(qiáng) 整數(shù)類型有Byte、Short、Int、Long,長(zhǎng)度分別為1、2、4、8字節(jié),對(duì)應(yīng)最大可以表示8、16、32、64位有符號(hào)補(bǔ)碼整數(shù)。Scala 的整型,默認(rèn)為 Int 型,聲明 Long 型,須后加l或L。
var n2:Byte = -128
var n3:Byte = 128 // 報(bào)錯(cuò),超過(guò)Byte有符號(hào)補(bǔ)碼所能表示的最大范圍
var n6 = 9223372036854775807L // 聲明Long類型
浮點(diǎn)類型有4字節(jié)32位的單精度浮點(diǎn)類型Float、8字節(jié)64位雙精度浮點(diǎn)數(shù)Double ,均遵循IEEE 754標(biāo)準(zhǔn)。默認(rèn)為 Double 型,聲明 Float 型常量,須后加f或F。
var n7 = 2.2345678912f
var n8 = 2.2345678912
println("n7=" + n7)
println("n8=" + n8)
/*運(yùn)行的結(jié)果
n7=2.2345679
n8=2.2345678912
*/
字符類型Char表示單個(gè)字符,長(zhǎng)度為2字節(jié)。字符常量是用單引號(hào) ’ ’ 括起來(lái)的單個(gè)字符
var c1: Char = 'a'
println("c1=" + c1) // 輸出c1=a
var c2: Char = 'a' + 1
println("c2=" + c2) //輸出c2=b
布爾類型Boolean占 1 個(gè)字節(jié),數(shù)據(jù)只允許取值 true 和 false
數(shù)據(jù)類型Unit表示無(wú)值,只有一個(gè)對(duì)象()。用作方法的返回值,對(duì)應(yīng)于Java中的返回值void,表示方法沒(méi)有返回值。
def main(args: Array[String]): Unit = { // main函數(shù)沒(méi)有返回值
}
Null只有一個(gè)對(duì)象就是null。它是所有引用類型(AnyRef)的子類,因此Null 可以賦值給任意引用類型(AnyRef),但是不能賦值給值類型(AnyVal)
var cat = new Cat();
cat = null // 正確
var n1: Int = null // 錯(cuò)誤
Nothing是所有數(shù)據(jù)類型的子類,主要在函數(shù)沒(méi)有明確返回值時(shí)將結(jié)果返回給任意變量接收,可以作為沒(méi)有正常返回值的方法的返回類型
def test() : Nothing={ // 返回拋出的異常
throw new Exception()
}
自動(dòng)類型轉(zhuǎn)換:當(dāng) Scala 程序在進(jìn)行賦值或者運(yùn)算時(shí),精度小的類型自動(dòng)轉(zhuǎn)換為精度大的數(shù)值類型,按照Byte、Short、Int、Long精度由低到高逐階提升。Byte不會(huì)向Char轉(zhuǎn),但是Char可以在進(jìn)行計(jì)算時(shí)轉(zhuǎn)為Int類型參與計(jì)算。
//多種類型的數(shù)據(jù)混合運(yùn)算時(shí),數(shù)據(jù)轉(zhuǎn)換成高精度類型再進(jìn)行計(jì)算。
var n = 1 + 2.0
println(n) // n為 Double
// Byte和Char轉(zhuǎn)為Int
var b: Byte = 1
var c: Char = 1
var i = b + c
println(i)
強(qiáng)制類型轉(zhuǎn)換可以將數(shù)據(jù)強(qiáng)制轉(zhuǎn)換為目標(biāo)類型,例如可以將數(shù)據(jù)由高精度轉(zhuǎn)換為低精度,使用時(shí)要加上強(qiáng)制轉(zhuǎn)函數(shù),但可能造成精度降低或溢出。強(qiáng)制轉(zhuǎn)換為數(shù)值類型時(shí)的函數(shù)為toInt、toDouble、toByte等類似函數(shù)。如果希望轉(zhuǎn)換為String類型只需要在基本類型值后+“”
// 向低精度轉(zhuǎn)換
var i: Int = 2.5.toInt
println(i) // 輸出2,精度損失
// 字符串轉(zhuǎn)數(shù)值類型
var s1 : String = "12"
var n1 : Byte = s1.toByte
// 轉(zhuǎn)換為字符串
var str1 : String = true + ""
2.5 運(yùn)算符
Scala 運(yùn)算符的使用和 Java 運(yùn)算符的使用基本相同
算術(shù)運(yùn)算符+、-、*、/、%,邏輯運(yùn)算符&&、||、!和Java一樣
關(guān)系運(yùn)算符 >、<、=、!=也相同,Scala中的==會(huì)比較二者的內(nèi)容,類似于Java中的equals
def main(args: Array[String]): Unit = {
val s1 = "abc"
val s2 = new String("abc")
println(s1 == s2) // true 二者中的內(nèi)容相同
println(s1.eq(s2)) // false 但不是同一塊內(nèi)存地址
}
需要注意的是Scala 中沒(méi)有++、–操作符,可以通過(guò)+=、-=來(lái)實(shí)現(xiàn)同樣的效果;
在 Scala 中其實(shí)是沒(méi)有運(yùn)算符的,所有運(yùn)算符都是方法,只不過(guò)在使用時(shí)省略了調(diào)用的.和()
// 標(biāo)準(zhǔn)的加法運(yùn)算
val i:Int = 1.+(1)
// (1)當(dāng)調(diào)用對(duì)象的方法時(shí),.可以省略
val j:Int = 1 + (1)
// (2)如果函數(shù)參數(shù)只有一個(gè),或者沒(méi)有參數(shù),()可以省略
val k:Int = 1 + 1
println(1.toString())
println(1 toString()) //省略.
println(1 toString) //省略()
2.6 流程控制
分支結(jié)構(gòu)
Scala的if分支控制的使用和Java相似
if (age < 18){
println("童年")
}else if(age>=18 && age<30){
println("中年")
}else{
println("老年")
}
Scala的if else表達(dá)式可以用于返回值
val res :String = if (age < 18){
"童年"
}else if(age>=18 && age<30){
"中年"
}else{
"老年"
}
可以用if else實(shí)現(xiàn)三元運(yùn)算符的效果
val res:Any = if (age < 18) "童年" else "成年"
需要注意的是在 Scala 中沒(méi)有 Switch,而是使用模式匹配來(lái)處理。模式匹配采用 match 關(guān)鍵字聲明,每個(gè)分支采用 case 關(guān)鍵字進(jìn)行聲明,匹配時(shí)從第一個(gè) case 分支開(kāi)始,如果匹配成功那么執(zhí)行對(duì)應(yīng)的邏輯代碼;否則繼續(xù)執(zhí)行下一個(gè)分支進(jìn)行判斷。如果所有 case 都不匹配,那么會(huì)執(zhí)行 case _ 分支。
var result = operator match {
case '+' => a + b
case '-' => a - b
case '*' => a * b
case '/' => a / b
case _ => "illegal"
}
模式匹配可以匹配所有的字面量,包括字符串,字符,數(shù)字,布爾值等,還可以對(duì)對(duì)象、集合等復(fù)雜類型進(jìn)行精確的匹配
val result = arr match {
case Array(0) => "0" //匹配 Array(0) 這個(gè)數(shù)組
case Array(x, y) => x + "," + y //匹配有兩個(gè)元素的數(shù)組,然后將將元素值賦給對(duì)應(yīng)的 x,y
case Array(0, _*) => "以 0 開(kāi)頭的數(shù)組" //匹配以 0 開(kāi)頭和數(shù)組
case _ => "something else"
}
循環(huán)結(jié)構(gòu)
Scala中的for循環(huán)有多種推導(dǎo)式
使用關(guān)鍵字to實(shí)現(xiàn)前后閉合的范圍內(nèi)數(shù)據(jù)遍歷
// i 取1、2、3
for(i <- 1 to 3){
print(i + " ")
}
until實(shí)現(xiàn)前閉后開(kāi)的遍歷
// i取1、2
for(i <- 1 until 3) {
print(i + " ")
}
循環(huán)守衛(wèi),即循環(huán)保護(hù)式(也稱條件判斷式,守衛(wèi))。保護(hù)式為 true 則進(jìn)入循環(huán)體內(nèi)部,為 false 則跳過(guò),類似于 continue。
//打印10以內(nèi)的偶數(shù)
for(i <- 1 to 10 if i % 2 == 0) {
print(i + " ")
}
循環(huán)步長(zhǎng)by,擱幾個(gè)數(shù)遍歷
// 擱3個(gè)數(shù)打印一次
for (i <- 1 to 10 by 3) {
println("i=" + i)
}
此外,在for的括號(hào)中可以進(jìn)行嵌套表達(dá)
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
}
// 上面代碼等價(jià)于
for (i <- 1 to 3) {
for (j <- 1 to 3) {
println("i =" + i + " j=" + j)
}
}
還可以在for推導(dǎo)式中引入變量
for(i <- 1 to 3; j = 4 - i) {
println("i=" + i + " j=" + j)
}
使用yield關(guān)鍵字可以將遍歷過(guò)程中處理的結(jié)果返回到一個(gè)新 Vector 集合中。
var res = for(i <-1 to 10) yield {
i * 2
}
println(res) //輸出Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
使用reverse關(guān)鍵字可以從后往前遍歷
// 倒序打印10到1
for(i <- 1 to 10 reverse){
println(i)
}
While 和 do…While 的使用和 Java 語(yǔ)言中用法相同。與 for 語(yǔ)句不同,while 語(yǔ)句沒(méi)有返回值.
Scala 內(nèi)置控制結(jié)構(gòu)特地去掉了 break 和 continue,是為了更好的適應(yīng)函數(shù)式編程,推薦使用函數(shù)式的風(fēng)格解決break和continue的功能,而不是一個(gè)關(guān)鍵字。Scala中使用breakable控制結(jié)構(gòu)來(lái)實(shí)現(xiàn) break 和 continue 功能。
Breaks.breakable(
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) Breaks.break()
}
)
//簡(jiǎn)略寫(xiě)法
breakable {
for (elem <- 1 to 10) {
println(elem)
if (elem == 5) break
}
}
3 函數(shù)式編程
Scala是一個(gè)將面向函數(shù)和面向?qū)ο缶幊掏昝廊诤系恼Z(yǔ)言。函數(shù)式編程是指將問(wèn)題分解成一個(gè)一個(gè)的步驟,將每個(gè)步驟封裝為函數(shù),通過(guò)調(diào)用這些封裝好的步驟解決問(wèn)題。Scala中的萬(wàn)物皆函數(shù),函數(shù)也可以當(dāng)作一個(gè)值進(jìn)行傳遞。
Scala中函數(shù)的定義語(yǔ)法如下
//關(guān)鍵字 函數(shù)名(參數(shù)名:參數(shù)類型):返回值類型
def sum(x: Int, y: Int): Int = {
// 函數(shù)體
x + y
}
3.1 函數(shù)與方法的區(qū)別
函數(shù)是指更為廣泛的未完成某一功能的程序語(yǔ)句的集合。具體地,和Java一樣,將類中的定義的函數(shù)稱之為方法,只能通過(guò)對(duì)象進(jìn)行調(diào)用。而Scala中的函數(shù)可以定義在任何地方,并且可以不借助對(duì)象直接使用。
如下所示,在Person類中首先定義了屬于該類的方法sayHi(),之后在main()方法中又定義了一個(gè)函數(shù)sayHi()。首先通過(guò)直接調(diào)用函數(shù)的方式對(duì)函數(shù)sayHi()進(jìn)行調(diào)用,之后通過(guò)對(duì)象調(diào)用方法sayHi()
object Person {
// 定義對(duì)象的方法
def sayHi(): Unit = {
println("調(diào)用對(duì)象方法sayHi")
}
def main(args: Array[String]): Unit = {
// 定義函數(shù)
def sayHi(): Unit = {
println("調(diào)用函數(shù)sayHi")
}
sayHi() // 輸出:調(diào)用函數(shù)sayHi
Person.sayHi() // 輸出:調(diào)用對(duì)象方法sayHi
}
}
區(qū)分清楚方法和函數(shù)之后需要注意,對(duì)象的方法可以進(jìn)行重載,但是函數(shù)卻沒(méi)有重載和重寫(xiě)的概念
object Person {
def sayHi(): Unit = {
println("調(diào)用對(duì)象方法sayHi")
}
// 方法可以進(jìn)行重載
def sayHi(name: String): Unit = {
println("重載sayHi,我是" + name)
}
def main(args: Array[String]): Unit = {
def sayHi(): Unit = {
println("調(diào)用函數(shù)sayHi")
}
// 函數(shù)無(wú)法重載,報(bào)錯(cuò):此范圍已定義sayHi
def sayHi(name: String): Unit = {
println("重載sayHi,我是" + name)
}
}
}
Scala 語(yǔ)言可以在任何的語(yǔ)法結(jié)構(gòu)中聲明任何的語(yǔ)法,因此可以在隨時(shí)進(jìn)行引入,并且進(jìn)行函數(shù)的嵌套
def main(args: Array[String]): Unit = {
// 隨時(shí)引入
import java.util.Date
new Date()
def test(): Unit ={
def test1(name:String):Unit={
println("函數(shù)可以嵌套定義")
}
}
}
函數(shù)的參數(shù):一般將有默認(rèn)值的參數(shù)放置在參數(shù)列表的后面,
def Person(name: String, sex: String = "男"): Unit = {
}
可變參數(shù)一般放置在最后
def Person(name: String, age: Int*): Unit = {
}
3.2 函數(shù)的省略
為了簡(jiǎn)便,Scala中經(jīng)常遇到函數(shù)省略寫(xiě)法
// 1 return 可以省略,默認(rèn)使用函數(shù)體的最后一行代碼作為返回值
def sum(x: Int, y: Int): Int = {
x + y
}
// 2 如果函數(shù)體只有一行代碼,可以省略花括號(hào)
def sum(x: Int, y: Int): Int = x + y
// 3 返回值類型如果能夠推斷出來(lái),那么可以省略
def sum(x: Int, y: Int) = x + y
// 如果有 return,則不能省略返回值類型
def sum(x: Int, y: Int): Int = {
return x + y
}
// 如果函數(shù)明確聲明 unit,那么 return 關(guān)鍵字不起作用
def fun(): Unit = {
return "return返回"
}
println(fun()) //輸出:()
// 4 如果無(wú)返回值類型,可以省略等號(hào)。無(wú)返回值的函數(shù)稱為過(guò)程
def fun() {
println("無(wú)返回值")
}
// 5 在調(diào)用具有參數(shù)列表的無(wú)參函數(shù)時(shí),括號(hào)可以省略
def fun() = "無(wú)參函數(shù)"
println(fun)
// 沒(méi)有參數(shù)列表的無(wú)參函數(shù),括號(hào)必須省略
def fun = "無(wú)參函數(shù)"
println(fun()) //報(bào)錯(cuò)
println(fun)
3.3 函數(shù)傳遞
函數(shù)不僅可以調(diào)用,還可以作為整體進(jìn)行傳遞。
如下所示定義函數(shù)func,在函數(shù)內(nèi)打印“直接調(diào)用函數(shù)”,并將字符串“函數(shù)返回值”返回。當(dāng)直接用變量res接收時(shí)會(huì)得到func的返回值;而在函數(shù)后面加上_相當(dāng)于把函數(shù)作為一個(gè)整體傳遞給變量f,之后f也可作為函數(shù)執(zhí)行。此外,通過(guò)() => String指定變量f2的類型為:輸入為空、返回值為String類型的函數(shù),那么下劃線也可以省略。
def func(): String = {
println("直接調(diào)用函數(shù)")
return "函數(shù)返回值"
}
val res = func()
println(res) // 輸出“函數(shù)返回值”
val f = func _
f() // 輸出:直接調(diào)用函數(shù)
// 直接傳遞函數(shù)
val f2: () => String = func
f2()
函數(shù)也可以作為參數(shù)值進(jìn)行傳遞。如下所示定義函數(shù)func1,函數(shù)func2接受一個(gè)函數(shù)作為參數(shù),并將其作為函數(shù)在其內(nèi)部調(diào)用。之后調(diào)用func2并將func1作為參數(shù)傳入,執(zhí)行結(jié)果如下。
def func1(): Unit = {
println("調(diào)用func1")
}
def func2(f: () => Unit) = {
print("在func2內(nèi)")
f() // 在函數(shù)內(nèi)部調(diào)用傳入的參數(shù)函數(shù)
}
func2(func1) // 輸出:在func2內(nèi)調(diào)用func1
函數(shù)也能作為返回值返回
def f1() = {
def f2() = {
}
f2 _ // 將函數(shù)作為返回值
}
匿名函數(shù)
就是指沒(méi)有名字的函數(shù),只關(guān)心函數(shù)的實(shí)現(xiàn)而不關(guān)注函數(shù)名。其形式為 (參數(shù)名:參數(shù)類型) => {函數(shù)體}
如下所示首先定義函數(shù)func,以輸入為String輸出為Unit的函數(shù)f作為形參,在其函數(shù)體內(nèi)調(diào)用作為參數(shù)的函數(shù)f。之后在使用func函數(shù)時(shí),將匿名函數(shù)作為參數(shù)傳入,這樣func在接收到參數(shù)后進(jìn)行執(zhí)行,輸出“在func內(nèi)調(diào)用匿名函數(shù)”
def func(f: String => Unit): Unit = {
f("在func內(nèi)調(diào)用")
}
func((s: String) => { println(s + "匿名函數(shù)")}) // 輸出:在func內(nèi)調(diào)用匿名函數(shù)
匿名函數(shù)有如下簡(jiǎn)寫(xiě)的情況
// 1 參數(shù)的類型可以省略,會(huì)根據(jù)形參進(jìn)行自動(dòng)的推導(dǎo)
func((s) => { println(s + "匿名函數(shù)")})
// 2 若只有一個(gè)參數(shù),則圓括號(hào)可以省略
func(s => { println(s + "匿名函數(shù)")})
// 3 函數(shù)如果只有一行,則大括號(hào)也可以省略
func(s => println(s + "匿名函數(shù)"))
// 4 如果參數(shù)只出現(xiàn)一次,則參數(shù)省略用_代替
func(println( _ ))
函數(shù)閉包
將函數(shù)中用到的所有變量和函數(shù)打包在一起保存起來(lái)。這樣在將函數(shù)作為指返回后,即使原來(lái)環(huán)境中的局部變量被釋放掉,函數(shù)仍然可以正常運(yùn)行。
函數(shù)層次化:為了降低函數(shù)的耦合度,將把一個(gè)參數(shù)列表的多個(gè)參數(shù)拆分為多個(gè)參數(shù)列表進(jìn)行傳遞。如下所示,將參數(shù)a、b拆分到了兩個(gè)列表
def f3(a: Int)(b: Int) = {
a + b
}
println(f3(2)(3))
傳名參數(shù)
當(dāng)函數(shù)作為變量進(jìn)行傳遞時(shí),傳遞的不是一個(gè)結(jié)果值,而是一個(gè)代碼塊,每次出現(xiàn)都會(huì)執(zhí)行對(duì)應(yīng)的代碼。在代碼塊作為參數(shù)時(shí)需要根據(jù)返回值指定類型,例如=>Int代表返回值為Int的代碼塊。
def f = () => {
println("f...")
10
}
def foo1(a: Int): Unit = {
println(a)
println(a)
}
foo1(f()) // 傳遞值,f只被調(diào)用一次
/* 輸出
f...
10
10
*/
def foo2(a: => Int): Unit = {
println(a)
println(a)
}
foo2(f()) // 傳遞函數(shù),f被調(diào)用了兩次
/* 輸出
f...
10
f...
10
*/
惰性加載當(dāng)函數(shù)返回值被聲明為 lazy 時(shí),函數(shù)的執(zhí)行將被推遲,直到我們首次對(duì)此取值,該函數(shù)才會(huì)執(zhí)行。這種函數(shù)我們稱之為惰性函數(shù)。
4 面向?qū)ο?/p>
Scala 的面向?qū)ο笏枷牒?Java 的面向?qū)ο笏枷牒透拍钍且恢碌?,并在此基礎(chǔ)上進(jìn)行了補(bǔ)充擴(kuò)展。
4.1 包管理
為了更好地區(qū)分類名,控制訪問(wèn)范圍,Scala也用包Package對(duì)類進(jìn)行管理。不同的是Scala可以在一個(gè)源文件中嵌套聲明多個(gè)package,這樣子包中的類可以直接訪問(wèn)父包中的內(nèi)容
package com{
package tory{
package scala{
}
}
}
關(guān)于包的使用注意以下幾點(diǎn)
在 Scala 中可以為每個(gè)包定義一個(gè)同名的包對(duì)象,其下所有 class 和 object 可以直接訪問(wèn)該包中的成員。和 Java 一樣,可以在頂部使用 import 導(dǎo)入,在這個(gè)文件中的所有類都可以使用。局部導(dǎo)入:什么時(shí)候使用,什么時(shí)候?qū)?。在其作用范圍?nèi)都可以使用通配符導(dǎo)入多個(gè)包:import java.util._給導(dǎo)入的類起名:import java.util.{ArrayList=>AL}屏蔽類:import java.util.{ArrayList =>,}導(dǎo)入一個(gè)包的多個(gè)類:import java.util.{HashSet, ArrayList}導(dǎo)入包的絕對(duì)路徑:new root.java.util.HashMap
4.2 類
一個(gè).scala 文件中可以定義多個(gè)類。Scala 中沒(méi)有 public,默認(rèn)可見(jiàn)性就是公開(kāi)的。但是其底層實(shí)現(xiàn)上是通過(guò)get 方法(obj.field())和 set 方法(obj.field_=(value))來(lái)實(shí)現(xiàn)對(duì)屬性的獲取和賦值的,因此Scala不推薦將屬性設(shè)置為private并通過(guò)getter、setter方法來(lái)操作。但有時(shí)為了和其他Java框架進(jìn)行兼容,也可以通過(guò)@BeanProperty注解為屬性設(shè)置getter和setter方法。
protected 為受保護(hù)權(quán)限,Scala 中受保護(hù)權(quán)限比 Java 中更嚴(yán)格,同類、子類可以訪問(wèn),同包無(wú)法訪問(wèn)。
class Person {
var name: String = "bobo" //定義屬性
var age: Int = _ // _表示給屬性一個(gè)默認(rèn)值,val 修飾的屬性不能賦默認(rèn)值,必須顯示指定
//Bean 屬性(@BeanProperty)
// 通過(guò)注解自動(dòng)生成規(guī)范的 setXxx/getXxx 方法
@BeanProperty
var sex: String = "男"
}
Scala 構(gòu)造對(duì)象也需要調(diào)用構(gòu)造方法,并且可以有任意多個(gè)構(gòu)造方法。Scala 類的構(gòu)造器包括:主構(gòu)造器和輔助構(gòu)造器。
主構(gòu)造器:由于類名和構(gòu)造函數(shù)名是一樣的,那么就可以把類名后面加上形參列表,從而當(dāng)作主構(gòu)造器。構(gòu)造器參數(shù)列表默認(rèn)為局部變量,如果用var修飾就是類成員屬性。如果主構(gòu)造器無(wú)參數(shù),小括號(hào)可省略,構(gòu)建對(duì)象時(shí)調(diào)用的構(gòu)造方法的小括號(hào)也可以省略??梢栽趨?shù)列表()前添加關(guān)鍵字private將主構(gòu)造器變?yōu)樗接?。輔助構(gòu)造器:函數(shù)的名稱 this,可以有多個(gè)進(jìn)行重載。輔助構(gòu)造器無(wú)法直接創(chuàng)建建對(duì)象,必須直接或者間接調(diào)用主構(gòu)造方法。
class Person(var name: String) { // 主構(gòu)造器
var age: Int = _
def this(name: String, age: Int) { // 輔助構(gòu)造器
this(name) // 調(diào)用主構(gòu)造器
this.age = age
}
def sayHi(): Unit = {
println("我叫" + name + ",年齡" + age)
}
}
object Person {
def main(args: Array[String]): Unit = {
val p1 = new Person("小李", 18)
p1.sayHi()
}
}
scala 是單繼承,子類繼承父類的屬性和方法,子類中調(diào)用父類的方法使用 super 關(guān)鍵字。如果重寫(xiě)需要通過(guò)關(guān)鍵字override聲明。
多態(tài)也是通過(guò)動(dòng)態(tài)綁定實(shí)現(xiàn)的。需要注意的是Scala 中屬性和方法都是動(dòng)態(tài)綁定,而 Java 中只有方法為動(dòng)態(tài)綁定。 如下所示,Teacher類為Person類的子類,通過(guò)val teacher1: Person = new Teacher創(chuàng)建一個(gè)Teacher對(duì)象,這里指針p的類型是Person,但是它并沒(méi)有和Person類綁定,而是在運(yùn)行過(guò)程中動(dòng)態(tài)判定其類型,在輸出屬性name和調(diào)用方法hello()時(shí)都輸出了teacher的信息。
class Person {
val name: String = "person"
def hello(): Unit = {
println("hello person")
}
}
class Teacher extends Person {
override val name: String = "teacher" // 重寫(xiě)父類屬性
override def hello(): Unit = { // 重寫(xiě)父類方法
println("hello teacher")
}
}
object Test {
def main(args: Array[String]): Unit = {
val teacher: Teacher = new Teacher()
println(teacher.name)
teacher.hello()
val p: Person = new Teacher
println(p.name) // 輸出teacher
p.hello() // 輸出hello teacher
}
}
如下所示為Java中多態(tài)的實(shí)現(xiàn),同樣定義了Person類的指針p,雖然其方法hello()輸出hello teacher但是其屬性名稱卻是person,可見(jiàn)其方法實(shí)現(xiàn)了動(dòng)態(tài)綁定,而屬性name卻沒(méi)有
public class TestDynamic {
public static void main(String[] args) {
Teacher teacher = new Teacher();
Person p = new Teacher();
System.out.println(teacher.name);
teacher.hello();
System.out.println(p.name); // 輸出person
p.hello(); // 輸出hello teacher
}
}
抽象類:在類屬性定義時(shí),如果一個(gè)屬性沒(méi)有初始化就是抽象屬性,只有在abstract關(guān)鍵字標(biāo)記的抽象類中才能定義抽象屬性和抽象方法,只聲明而沒(méi)有實(shí)現(xiàn)的方法就是抽象方法。如果父類為抽象類,那么子類需要將抽象的屬性和方法實(shí)現(xiàn),否則子類也需聲明為抽象類。
如下所示為三個(gè)類方法
//(1)判斷對(duì)象是否為某個(gè)類型的實(shí)例
val bool: Boolean = person.isInstanceOf[Person]
if ( bool ) {
//(2)將對(duì)象轉(zhuǎn)換為某個(gè)類型的實(shí)例
val p1: Person = person.asInstanceOf[Person]
println(p1)
}
//(3)獲取類的信息
val pClass: Class[Person] = classOf[Person]
枚舉類需要繼承Enumeration,應(yīng)用類需要繼承App
object Color extends Enumeration {
val RED = Value(1, "red")
val YELLOW = Value(2, "yellow")
val BLUE = Value(3, "blue")
}
4.3 單例對(duì)象
Scala語(yǔ)言是完全面向?qū)ο蟮恼Z(yǔ)言,所以并沒(méi)有靜態(tài)的概念。但是為了能夠和Java語(yǔ)言交互,就產(chǎn)生了一種特殊的對(duì)象來(lái)模擬類對(duì)象,該對(duì)象為單例對(duì)象。若單例對(duì)象名與類名一致,則稱該單例對(duì)象這個(gè)類的伴生對(duì)象,這個(gè)類的所有“靜態(tài)”內(nèi)容都可以放置在它的伴生對(duì)象中聲明。
單例對(duì)象采用 object 關(guān)鍵字聲明,單例對(duì)象對(duì)應(yīng)的類稱之為伴生類,伴生對(duì)象的名稱應(yīng)該和伴生類名一致。單例對(duì)象中的屬性和方法都可以通過(guò)伴生對(duì)象名(類名)直接調(diào)用訪問(wèn)。
class Person { // 伴生類
var name: String = "bobo"
}
object Person { // 伴生對(duì)象
var country: String = "China"
}
object Test {
def main(args: Array[String]): Unit = {
// 通過(guò)伴生對(duì)象訪問(wèn)屬性
println(Person.country)
}
}
可以通過(guò)函數(shù)式的風(fēng)格創(chuàng)建對(duì)象,即類名(),其本質(zhì)是在調(diào)用該對(duì)象的 apply() 方法,在apply中調(diào)用構(gòu)造器,進(jìn)而統(tǒng)一面向?qū)ο缶幊毯秃瘮?shù)式編程的風(fēng)格
class Person {
println("調(diào)用主構(gòu)造器")
}
object Person {
def apply(): Person = {
println("調(diào)用apply")
new Person()
}
}
object Test {
def main(args: Array[String]): Unit = {
var p = Person() // 用函數(shù)式的風(fēng)格創(chuàng)建對(duì)象
/* 輸出
調(diào)用apply
調(diào)用主構(gòu)造器
*/
}
}
4.4 Trait
Scala 語(yǔ)言中采用特質(zhì) trait(特征)來(lái)代替接口的概念,即將具有相同特征的多個(gè)類抽象出來(lái),聲明為trait
trait 中即可以有抽象屬性和方法,也可以有具體的屬性和方法。
trait PersonTrait {
// 聲明屬性
var name:String = _
// 聲明方法
def eat():Unit={
}
// 抽象屬性
var age:Int
// 抽象方法
def say():Unit
}
與Java只能實(shí)現(xiàn)一個(gè)接口不同,一個(gè)類可以混入(mixin)多個(gè)特質(zhì),這也是對(duì)Scala單繼承機(jī)制的一種補(bǔ)充。如果有多個(gè)特質(zhì)或存在父類,那么需要采用 with關(guān)鍵字連接。所有的 Java 接口都可以當(dāng)做 Scala 特質(zhì)使用。還可以在創(chuàng)建對(duì)象時(shí)動(dòng)態(tài)混入trait。
// 繼承多個(gè)trait
class Teacher extends PersonTrait with java.io.Serializable {
override def say(): Unit = {
println("say")
}
override var age: Int = _
}
object TestTrait {
def main(args: Array[String]): Unit = {
// 動(dòng)態(tài)混入:靈活擴(kuò)展對(duì)象
val t2 = new Teacher with SexTrait {
override var sex: String = "男"
}
//調(diào)用混入 trait 的屬性
println(t2.sex)
}
}
當(dāng)混入的特質(zhì)中具有相同的方法造成沖突時(shí):
如果兩個(gè) trait 之間沒(méi)有任何關(guān)系,則直接在中重寫(xiě)沖突方法兩個(gè) trait A、B繼承自相同的 trait C,采用特質(zhì)疊加:將混入的多個(gè) trait 中的沖突方法疊加起來(lái),依次執(zhí)行A、B、C的部分
4.5 泛型
Scala在傳統(tǒng)泛型的基礎(chǔ)上還針對(duì)集合增加了協(xié)變和逆變
協(xié)變:Son 是 Father 的子類,則 MyList[Son] 也作為 MyList[Father]的“子類”。逆變:Son 是 Father 的子類,則 MyList[Son]作為 MyList[Father]的“父類”。
class MyList[+T]{ //協(xié)變
}
class MyList[-T]{ //逆變
}
class MyList[T] //不變
泛型還可以通過(guò)上下限對(duì)傳入的類型進(jìn)行限定,上限就是指?jìng)魅氲膮?shù)
Class PersonList[T <: Person]{ //泛型上限,傳入的類型T為Person的子類或自身
}
Class PersonList[T >: Person]{ //泛型下限,傳入Person或其父類
}
5 集合
Scala 的集合有三大類:序列 Seq、集 Set、映射 Map,所有的集合都擴(kuò)展自 Iterable。對(duì)于幾乎所有的集合類,Scala 都同時(shí)提供了可變和不可變的版本
不可變集合:scala.collection.immutable。集合對(duì)象不可修改,每次修改就會(huì)返回一個(gè)新對(duì)象,類似于 java 中的 String 對(duì)象可變集合: scala.collection.mutable??梢灾苯訉?duì)原對(duì)象進(jìn)行修改,而不會(huì)返回新的對(duì)象。類似于 java 中 StringBuilder 對(duì)象
如下所示為不可變集合繼承圖 下圖為可變集合
集合常見(jiàn)的共有函數(shù)如下所示
//(1)獲取集合長(zhǎng)度
println(list.length)
//(2)獲取集合大小,等同于 length
println(list.size)
//(3)循環(huán)遍歷
list.foreach(println)
//(4)迭代器
for (elem <- list.itera tor) {
println(elem)
}
//(5)生成字符串
println(list.mkString(","))
//(6)是否包含
println(list.contains(3))
5.1 數(shù)組
如下所示為對(duì)不可變數(shù)組Array進(jìn)行定義和訪問(wèn)的常見(jiàn)方法
// 1 數(shù)組定義,長(zhǎng)度為4的Int型數(shù)組
val arr01 = new Array[Int](4)
println(arr01.length) // 4
// 1.2 使用函數(shù)式的apply方法創(chuàng)建數(shù)組并直接賦初值
val arr1 = Array(1, 2)
//(2)數(shù)組賦值
//(2.1)修改某個(gè)元素的值
arr01(3) = 10
//(2.2)采用方法的形式給數(shù)組賦值
arr01.update(0,1)
//(3)遍歷數(shù)組
//(3.1)打印數(shù)組
println(arr01.mkString(","))
//(3.2)普通遍歷
for (i <- arr01) {
println(i)
}
//(3.3)對(duì)每個(gè)元素使用函數(shù)
def printx(elem:Int): Unit = {
println(elem)
}
arr01.foreach(printx)
arr01.foreach(println)
//(4)增加元素(由于創(chuàng)建的是不可變數(shù)組,增加元素其實(shí)是產(chǎn)生新的數(shù)組)
val ints: Array[Int] = arr01 :+ 5
println(ints)
如下所示為可變數(shù)組ArrayBuffer的使用
//(1)創(chuàng)建并初始賦值可變數(shù)組
val arr01 = ArrayBuffer[Any](1, 2, 3)
println(arr01.length) // 3
println("arr01.hash=" + arr01.hashCode())
//(2)遍歷數(shù)組
for (i <- arr01) {
println(i)
}
//(3)增加元素
//(3.1)追加數(shù)據(jù)
arr01.+=(4)
//(3.2)向數(shù)組最后追加數(shù)據(jù)
arr01.append(5,6)
//(3.3)向指定的位置插入數(shù)據(jù)
arr01.insert(0,7,8)
println("arr01.hash=" + arr01.hashCode()) // 數(shù)組的哈希值不變
//(4)修改元素
arr01(1) = 9 //修改第 2 個(gè)元素的值
通過(guò)arr1.toBuffer 可以將不可變數(shù)組轉(zhuǎn)可變數(shù)組,arr2.toArray 將可變數(shù)組轉(zhuǎn)不可變數(shù)組
如下所示為多維數(shù)組的創(chuàng)建和使用
// 創(chuàng)建一個(gè)3×4的二維數(shù)組
val arr = Array.ofDim[Int](3, 4)
arr(1)(2) = 88
//(2)遍歷二維數(shù)組
for (a <- arr) { //a 就是一維數(shù)組
for (i <- a) {
print(i + " ")
}
println()
}
5.2 List
如下所示為不可變List的創(chuàng)建和使用
//創(chuàng)建一個(gè) List(數(shù)據(jù)有順序,可重復(fù))
val list: List[Int] = List(1,2,3,4,3)
// 訪問(wèn)元素
println(list(0))
// 遍歷
list.foreach(println)
// 增加數(shù)據(jù)
val list2 = list.+:(5)
// 將元素5添加到集合,運(yùn)算規(guī)則從右向左
val list1 = 5::list
// 將集合list拆分成單個(gè)元素后逐個(gè)添加到list1
val list2 = list ::: list1
如下所示為可變 ListBuffer的創(chuàng)建和使用
//(1)創(chuàng)建一個(gè)可變集合
val buffer = ListBuffer(1,2,3,4)
//(2)向集合中添加數(shù)據(jù)
buffer.+=(5)
buffer.append(6)
buffer.insert(1,2)
//(3)打印集合數(shù)據(jù)
buffer.foreach(println)
//(4)修改數(shù)據(jù)
buffer(1) = 6
buffer.update(1,7)
//(5)刪除數(shù)據(jù)
buffer.-(5)
5.3 Set集合
如下所示為不可變集合Set的使用
//(1)Set 默認(rèn)是不可變集合,數(shù)據(jù)無(wú)序
val set = Set(1,2,3,4,5,6)
//(2)數(shù)據(jù)不可重復(fù)
val set1 = Set(1,2,3,4,5,6,3)
//(3)遍歷集合
for(x<-set1){
println(x)
}
默認(rèn)情況下Scala 使用的是不可變集合,如果想使用可變集合需要引用scala.collection.mutable.Set
//(1)創(chuàng)建可變集合
val set = mutable.Set(1,2,3,4,5,6)
//(3)集合添加元素
set += 8
//(4)向集合中添加元素,返回一個(gè)新的 Set
val ints = set.+(9)
//(5)刪除數(shù)據(jù)
set-=(5)
5.4 Map集合
如下所示為不可變集合Map
//(1)創(chuàng)建不可變集合 Map
val map = Map( "a"->1, "b"->2, "c"->3 )
//(2)循環(huán)打印
map.foreach((kv)=>{println(kv)})
//(3)訪問(wèn)數(shù)據(jù)
for (elem <- map.keys) {
// 使用 get 訪問(wèn) map 集合的數(shù)據(jù)
println(elem + "=" + map.get(elem).get)
}
//(4)如果 key 不存在,返回 0
println(map.get("d").getOrElse(0))
println(map.getOrElse("d", 0))
使用可變集合需要引入mutable.Map
//(1)創(chuàng)建可變集合
val map = mutable.Map( "a"->1, "b"->2, "c"->3 )
//(3)向集合增加數(shù)據(jù)
map.+=("d"->4)
// 將數(shù)值 4 添加到集合,并把集合中原值 1 返回
val maybeInt: Option[Int] = map.put("a", 4)
println(maybeInt.getOrElse(0))
//(4)刪除數(shù)據(jù)
map.-=("b", "c")
//(5)修改數(shù)據(jù)
map.update("d",5)
map("d") = 5
5.5 集合函數(shù)
集合集成了許多常用的操作函數(shù)
//(1)獲取集合的頭
println(list1.head)
//(2)獲取集合的尾(不是頭的就是尾)
println(list1.tail)
//(3)集合最后一個(gè)數(shù)據(jù)
println(list1.last)
//(4)集合初始數(shù)據(jù)(不包含最后一個(gè))
println(list1.init)
//(5)反轉(zhuǎn)
println(list1.reverse)
//(6)取前(后)n 個(gè)元素
println(list1.take(3))
println(list1.takeRight(3))
//(7)去掉前(后)n 個(gè)元素
println(list1.drop(3))
println(list1.dropRight(3))
//(8)并集
println(list1.union(list2))
//(9)交集
println(list1.intersect(list2))
//(10)差集
println(list1.diff(list2))
//(11)拉鏈 注:如果兩個(gè)集合的元素個(gè)數(shù)不相等,那么會(huì)將同等數(shù)量的數(shù)據(jù)進(jìn)行拉鏈,多余的數(shù)據(jù)省略不用
println(list1.zip(list2))
//(12)滑窗
list1.sliding(2, 5).foreach(println)
還有一些計(jì)算有關(guān)的函數(shù)
//(1)求和
println(list.sum)
//(2)求乘積
println(list.product)
//(3)最大值
println(list.max)
//(4)最小值
println(list.min)
//(5)排序
// (5.1)按照元素大小排序
println(list.sortBy(x => x))
// (5.2)按照元素的絕對(duì)值大小排序
println(list.sortBy(x => x.abs))
// (5.3)按元素大小升序排序
println(list.sortWith((x, y) => x < y))
// (5.4)按元素大小降序排序
println(list.sortWith((x, y) => x > y))
如下所示為對(duì)集合元素進(jìn)行操作的函數(shù)
//(1)過(guò)濾
println(list.filter(x => x % 2 == 0))
//(2)轉(zhuǎn)化/映射
println(list.map(x => x + 1))
//(3)扁平化
println(nestedList.flatten)
//(4)扁平化+映射 注:flatMap 相當(dāng)于先進(jìn)行 map 操作,在進(jìn)行 flatten操作
println(wordList.flatMap(x => x.split(" ")))
//(5)分組
println(list.groupBy(x => x % 2))
// 6 聚合
val list = List(1,2,3,4)
val i1 = list.reduceLeft((x,y) => x-y)
val i2 = list.reduceRight((x,y) => x-y) // 4-3-2-1=-2
6 異常
Scala的異常處理和Java類似,將可疑代碼封裝在 try 塊中,之后使用了一個(gè) catch 處理程序來(lái)捕獲異常。如果發(fā)生任何異常,catch 處理程序?qū)⑻幚硭绦驅(qū)⒉粫?huì)異常終止。最后無(wú)論是否異常都執(zhí)行finally子句。
try {
var n= 10 / 0
}catch {
case ex: ArithmeticException=>{
// 發(fā)生算術(shù)異常
println("發(fā)生算術(shù)異常")
}
case ex: Exception=>{
// 對(duì)異常處理
println("發(fā)生了異常 1")
println("發(fā)生了異常 2")
}
}finally {
println("finally")
}
但是 Scala 沒(méi)有“checked(編譯期)”異常, 即 Scala 沒(méi)有編譯異常這個(gè)概念,異常都是在運(yùn)行的時(shí)候捕獲處理。
用 throw 關(guān)鍵字,拋出一個(gè)異常對(duì)象。所有異常都是 Throwable 的子類型。throw 表達(dá)式是有類型的,就是 Nothing,因?yàn)?Nothing 是所有類型的子類型,所以 throw 表達(dá)式可以用在需要類型的地方
def test():Nothing = {
throw new Exception("異常")
}
柚子快報(bào)邀請(qǐng)碼778899分享:Scala語(yǔ)言的特性
精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。