Scala基礎(chǔ)語法總結(jié)
一、變量
val/var 變量名稱:變量類型 = 初始值
注意:
val
定義的是不可重新賦值的變量(值不可修改)var
定義的是可重新賦值的變量(值可以修改)
scala中聲明變量是變量名稱在前,變量類型在后,跟java是正好相反
scala的語句最后不需要添加分號(hào)、、
惰性變量
Scala中使用==關(guān)鍵字lazy==來定義惰性變量,實(shí)現(xiàn)延遲加載(懶加載)。
惰性變量只能是不可變變量,并且只有在調(diào)用惰性變量時(shí),才會(huì)去實(shí)例化這個(gè)變量。
語法格式
lazy val 變量名 = 表達(dá)式
二、數(shù)據(jù)類型
基礎(chǔ)類型 | 類型說明 |
---|---|
Byte | 8位帶符號(hào)整數(shù) |
Short | 16位帶符號(hào)整數(shù) |
Int | 32位帶符號(hào)整數(shù) |
Long | 64位帶符號(hào)整數(shù) |
Char | 16位無符號(hào)Unicode字符 |
String | Char類型的序列(字符串) |
Float | 32位單精度浮點(diǎn)數(shù) |
Double | 64位雙精度浮點(diǎn)數(shù) |
Boolean | true或false |
1. scala中所有的類型都使用大寫字母開頭,說明是它是“類”
2. 整形使用Int而不是Integer
3. scala中定義變量可以不寫類型,讓scala編譯器自動(dòng)推斷
三、scala中的條件表達(dá)式
1、if表達(dá)式
這個(gè)和java差不多,可以加括號(hào),或者不加括號(hào)
def main(args: Array[String]): Unit = { val x:Int=2 if (x>10) println(x) else println(x+x) }
打印為2
2、塊表達(dá)式
定義變量時(shí)用 {} 包含一系列表達(dá)式,其中塊的最后一個(gè)表達(dá)式的值就是塊的值
def main(args: Array[String]): Unit = { val x = 0 val result = { val y = x + 10 val z = y + "-hello" val m = z + "-kaikeba" "over" } println(result) }
打印結(jié)果
over Process finished with exit code 0
返回值就是m了
def main(args: Array[String]): Unit = { val x = 0 val result = { val y = x + 10 val z = y + "-hello" val m = z + "-kaikeba" m } println(result) }
打印結(jié)果
10-hello-kaikeba
四、循環(huán)
在scala中,可以使用for和while,但一般推薦使用for表達(dá)式,因?yàn)閒or表達(dá)式語法更簡(jiǎn)潔
for (i <- 表達(dá)式/數(shù)組/集合){ //表達(dá)式 }
1、簡(jiǎn)單for循環(huán)
def main(args: Array[String]): Unit = { var nums=1 to 10 //for循環(huán) for(i <- nums) println(i) }
2、雙層for循環(huán)
def main(args: Array[String]): Unit = { //雙層for循環(huán) for (i <- 1 to 3; j <- 1 to 3) println(i * 10 + j) }
打印的值為
11 12 13 21 22 23 31 32 33
3、守衛(wèi):在for表達(dá)式中可以添加if判斷語句,這個(gè)if判斷稱為守衛(wèi)
def main(args: Array[String]): Unit = { // 守衛(wèi) var nums = 1 to 10 for (i <- nums if i>5 ) println(i) }
打印的值為:
6 7 8 9 10
4、yield表達(dá)式
在for循環(huán)體中,以yield表達(dá)式開始,這類循環(huán)能構(gòu)建出一個(gè)新的集合,我們把這類循環(huán)稱為推導(dǎo)式
def main(args: Array[String]): Unit = { // for推導(dǎo)式:for表達(dá)式中以yield開始,該for表達(dá)式會(huì)構(gòu)建出一個(gè)集合 val v = for(i <- 1 to 5) yield i * 10 //打印集合v的第一個(gè)元素 println(v(0)) }
打印的值為
10
五、while循環(huán)
while(返回值為布爾類型的表達(dá)式){ //表達(dá)式 }
六、方法
def methodName (參數(shù)名:參數(shù)類型, 參數(shù)名:參數(shù)類型) : [return type] = { // 方法體:一系列的代碼 }
參數(shù)列表的參數(shù)類型不能省略
返回值類型可以省略,由scala編譯器自動(dòng)推斷
返回值可以不寫return,默認(rèn)就是{}塊表達(dá)式的值
注意:
如果定義遞歸方法,必須指定返回值類型
示例:(方法三)
定義遞歸方法(求階乘)
10 * 9 * 8 * 7 * 6 * ... * 1
//方法一 def yy(i: Int = 10, u: Int = 8): Int = { return i + u; } //方法二 def tt(i: Int) = i * i /** * 遞歸必須添加返回值參數(shù) * * @param x * @return */ //方法三 def m1(x: Int): Int = { if (x == 1) 1 else x * m1(x - 1) } /** * 參數(shù)值加*,可以傳遞多個(gè)參數(shù) * * @param num * @return */ // 方法四 def add(num: Int*) = num.sum def main(args: Array[String]): Unit = { println(yy(1)) println(tt(8)) println(m1(10)) println(add(1,2,3)) }
打印的值為
9 64 3628800 6
6、函數(shù)
函數(shù)在scala中屬于頭等公民
數(shù)字能做的事,函數(shù)也可以
數(shù)字可以作為參數(shù),所以函數(shù)也可以作為其他方法或函數(shù)的參數(shù)
數(shù)字可以作為返回值,所以函數(shù)也可以作為其他方法或函數(shù)的返回值
數(shù)字可以賦值給一個(gè)變量,所以函數(shù)也可以賦值給一個(gè)變量
scala支持函數(shù)式編程,將來編寫Spark/Flink程序中,會(huì)大量使用到函數(shù)
語法
val 函數(shù)變量名 = (參數(shù)名:參數(shù)類型, 參數(shù)名:參數(shù)類型....) => 函數(shù)體
注意
函數(shù)是一個(gè)對(duì)象(變量)
類似于方法,函數(shù)也有輸入?yún)?shù)和返回值
函數(shù)定義不需要使用def定義
無需指定返回值類型
val add2 = (x: Int, y: Int) => x * y def main(args: Array[String]): Unit = { println(add2(2,4)) }
打印的值為
8
方法和函數(shù)的區(qū)別
方法是隸屬于類或者對(duì)象的,在運(yùn)行時(shí),它是加載到JVM的==方法區(qū)==中
可以將函數(shù)對(duì)象賦值給一個(gè)變量,在運(yùn)行時(shí),它是加載到JVM的==堆內(nèi)存==中
函數(shù)是一個(gè)對(duì)象,繼承自FunctionN,函數(shù)對(duì)象有apply,curried,toString,tupled這些方法,而方法則沒有
方法轉(zhuǎn)換為函數(shù)
有時(shí)候需要將方法轉(zhuǎn)換為函數(shù),作為變量傳遞,就需要將方法轉(zhuǎn)換為函數(shù)
使用
_
即可將方法轉(zhuǎn)換為函數(shù)(記得空格)//方法三 def m1(x: Int): Int = { if (x == 1) 1 else x * m1(x - 1) } val hanshu=m1 _; def main(args: Array[String]): Unit = { println(hanshu(10)) }
打印的結(jié)果
3628800
7、數(shù)組
scala中數(shù)組的概念是和Java類似,可以用數(shù)組來存放同類型的一組數(shù)據(jù)
scala中,有兩種數(shù)組,一種是定長(zhǎng)數(shù)組,另一種是變長(zhǎng)數(shù)組
(1)、定長(zhǎng)數(shù)組
定長(zhǎng)數(shù)組指的是數(shù)組的長(zhǎng)度是不允許改變的
數(shù)組的元素是可以改變的
demo如下
def main(args: Array[String]): Unit = { val a = new Array[Int](10) println(a) a(0)=98 println(a(0)) println(a(1)) println(a.length) }
打印的值為:
[I@ea4a92b 98 0 10
(2)、變長(zhǎng)數(shù)組
變長(zhǎng)數(shù)組指的是數(shù)組的長(zhǎng)度是可變的,可以往數(shù)組中添加、刪除元素
創(chuàng)建變長(zhǎng)數(shù)組,需要提前導(dǎo)入ArrayBuffer類
import scala.collection.mutable.ArrayBuffer
語法
創(chuàng)建空的ArrayBuffer變長(zhǎng)數(shù)組
val/var a = ArrayBuffer[元素類型]()
創(chuàng)建帶有初始元素的ArrayBuffer
val/var a = ArrayBuffer(元素1,元素2,元素3....)
變長(zhǎng)數(shù)組的增刪改操作
使用
+=
添加元素使用
-=
刪除元素使用
++=
追加一個(gè)數(shù)組到變長(zhǎng)數(shù)組
demo如下
def main(args: Array[String]): Unit = { //變長(zhǎng)數(shù)組 val a = ArrayBuffer[String](); a+=("test") a+=("張三") a++=Array("妞兒","所以算是") println(a(3)) println(a.size) } }
打印的值為
所以算是 4
(3)、遍歷數(shù)組
可以使用以下兩種方式來遍歷數(shù)組:
使用==for表達(dá)式== 直接遍歷數(shù)組中的元素
使用 ==索引== 獲得數(shù)組中的元素
for(i <- a) println(i)
/0 to n ——包含0,也包含n for(i <- 0 to a.length -1 ) println(a(i))
//0 until n ——生成一系列的數(shù)字,包含0,不包含n for(i <- 0 until a.length) println(a(i))
(4)、數(shù)組常用操作
scala中的數(shù)組封裝了豐富的計(jì)算操作,將來在對(duì)數(shù)據(jù)處理的時(shí)候,不需要我們自己再重新實(shí)現(xiàn)。
求和——sum方法
求最大值——max方法
求最小值——min方法
排序——sorted方法
def main(args: Array[String]): Unit = { val array = Array(1,3,4,2,5) println("求和:"+array.sum) println("最大值:"+array.max) println("最小值:"+array.min) println("排序(獲取一個(gè)新數(shù)組,并翻轉(zhuǎn)):"+array.sorted.reverse(0)) }
打印如下
求和:15 最大值:5 最小值:1 排序(獲取一個(gè)新數(shù)組,并翻轉(zhuǎn)):5
8、元組
元組可以用來包含一組不同類型的值。例如:姓名,年齡,性別,出生年月。
元組的元素是不可變的。
1、定義元組
使用括號(hào)來定義元組
val/var 元組變量名稱 = (元素1, 元素2, 元素3....)
使用箭頭來定義元素(元組只有兩個(gè)元素 )
val/var 元組 = 元素1 -> 元素2
2、訪問元組
使用
_1、_2、_3....
來訪問元組中的元素元組的index從1開始,_1表示訪問第一個(gè)元素,依次類推
def main(args: Array[String]): Unit = { val a = (1, "張三", 20, "北京市") val b = 1 -> 2 println(a._1) println(a._4) println(b._1) }
打印的值為
1 北京市 1
9、映射Map
Map可以稱之為映射。它是由鍵值對(duì)組成的集合。scala當(dāng)中的Map集合與java當(dāng)中的Map類似,也是key,value對(duì)形式的。
在scala中,Map也分為不可變Map和可變 Map。
(1)、不可變map
定義語法
val/var map = Map(鍵->值, 鍵->值, 鍵->值...) // 推薦這種寫法,可讀性更好 val/var map = Map((鍵, 值), (鍵, 值), (鍵, 值), (鍵, 值)...)
def main(args: Array[String]): Unit = { val map1 = Map("zhangsan"->30, "lisi"->40) val map2 = Map(("zhangsan", 30), ("lisi", 30)) println(map1("lisi")) println(map2("zhangsan")) }
打印的值為
40 30
(2)、可變Map
1、導(dǎo)包
import scala.collection.mutable.Map
def main(args: Array[String]): Unit = { val map3 = Map("zhangsan" -> 30, "lisi" -> 40) //修改一個(gè) map3("zhangsan") = 50 println("修改的" + map3) //添加一個(gè) map3 += ("yy" -> 22) println("添加的" + map3) //刪去一個(gè) map3 -= "yy" println("刪除的" + map3) //拿取到map所有key println("拿取到map所有key:" + map3.keys) println("拿取到map所有key:" + map3.keySet) //獲取所有的value println("獲取所有的value:" + map3.values) }
打印的值為:
修改的Map(lisi -> 40, zhangsan -> 50) 添加的Map(yy -> 22, lisi -> 40, zhangsan -> 50) 刪除的Map(lisi -> 40, zhangsan -> 50) 拿取到map所有key:Set(lisi, zhangsan) 拿取到map所有key:Set(lisi, zhangsan) 獲取所有的value:HashMap(40, 50)
(3)、遍歷Map
val map3 = Map("zhangsan" -> 30, "lisi" -> 40) //方法一。通過遍歷key拿取到值 for (i <- map3.keys) println(i + "->" + map3(i)) //方法二,通過元組的方法拿取到值 for (i <- map3) println(i._1 + "->" + i._2) //方法三 for((k, v) <- map3) println(k + " -> " + v)
lisi->40 zhangsan->30 lisi->40 zhangsan->30 lisi -> 40 zhangsan -> 30
10、Set集合
Set是代表沒有重復(fù)元素的集合。
Set具備以下性質(zhì):
1、元素不重復(fù)
2、不保證插入順序
scala中的set集合也分為兩種,一種是不可變集合,另一種是可變集合。
不可變set集合
//創(chuàng)建一個(gè)空的不可變集 val/var 變量名 = Set[類型]() //給定元素來創(chuàng)建一個(gè)不可變集 val/var 變量名 = Set[類型](元素1, 元素2, 元素3...)
def main(args: Array[String]): Unit = { val a = Set(1, 1, 2, 3, 4, 5) println("a的長(zhǎng)度大小"+a.size) for(i<-a)println(i) println("添加一個(gè)元素的新set:",a + 6) println("刪除一個(gè)元素的新set:",a -1) println("刪除多個(gè)元素的新set:",a -- Set(2,3) ) println("添加多個(gè)元素的新set:",a ++ Set(6,7,8) ) println("多個(gè)Set集合交集的新set:",a & Set(3,4,5,6)) println(a) }
打印如下:
a的長(zhǎng)度大小5 5 1 2 3 4 (添加一個(gè)元素的新set:,Set(5, 1, 6, 2, 3, 4)) (刪除一個(gè)元素的新set:,Set(5, 2, 3, 4)) (刪除多個(gè)元素的新set:,Set(5, 1, 4)) (添加多個(gè)元素的新set:,Set(5, 1, 6, 2, 7, 3, 8, 4)) (多個(gè)Set集合交集的新set:,Set(5, 3, 4)) Set(5, 1, 2, 3, 4)
注意:這里對(duì)不可變的set集合進(jìn)行添加刪除等操作,對(duì)于該集合來說是沒有發(fā)生任何變化,這里是生成了新的集合,新的集合相比于原來的集合來說發(fā)生了變化
可變Set集合
要使用可變集,必須要手動(dòng)導(dǎo)入:
import scala.collection.mutable.Set
11、列表 List
List是scala中最重要的、也是最常用的數(shù)據(jù)結(jié)構(gòu)。
List具備以下性質(zhì):
1、可以保存重復(fù)的值
2、有先后順序
在scala中,也有兩種列表,一種是不可變列表、另一種是可變列表
不可變列表就是列表的元素、長(zhǎng)度都是不可變的
語法
不可變列表
- 使用 List(元素1, 元素2, 元素3, ...) 來創(chuàng)建一個(gè)不可變列表,語法格式
val/var 變量名 = List(元素1, 元素2, 元素3...) //使用 Nil 創(chuàng)建一個(gè)不可變的空列表 val/var 變量名 = Nil //使用 :: 方法創(chuàng)建一個(gè)不可變列表 val/var 變量名 = 元素1 :: 元素2 :: Nil
def main(args: Array[String]): Unit = { val list1 = List(1, 2, 3, 4) val list2 = Nil val list3= 1::2::3::Nil println(list1(0)) println(list3) }
打印的值為
1 List(1, 2, 3)
可變列表
1、使用ListBuffer元素類型 創(chuàng)建空的可變列表,語法結(jié)構(gòu)
val/var 變量名 = ListBuffer[Int]()
2、使用ListBuffer(元素1, 元素2, 元素3...)創(chuàng)建可變列表,語法結(jié)構(gòu)
val/var 變量名 = ListBuffer(元素1,元素2,元素3...)
val a = ListBuffer[Int]() val b = ListBuffer(1, 2, 3, 4) println(b(0)) println("list數(shù)組首部:", b.head) println("獲取除了第一個(gè)元素外其他元素組成的列表", b.tail) b += 5 println("添加應(yīng)元素", b) b ++= List(6, 7) println("添加一個(gè)不可變列表", b) b ++= ListBuffer(8, 9) println("添加一個(gè)可變列表", b) b -= 9 println("刪除單個(gè)元素", b) b --= List(7,8) println("刪除一個(gè)不可變的列表存在的元素", b) b --= ListBuffer(5,6) println("刪除一個(gè)可變的列表存在的元素", b) println("toList根據(jù)可變的列表生成一個(gè)不可變列表",b.toList) println("toList根據(jù)可變的列表生成一個(gè)不可變列表,原列表不變",b) println("toArray根據(jù)可變的列表生成一個(gè)新的不可變數(shù)組",b.toArray) println("toArray根據(jù)可變的列表生成一個(gè)新的不可變數(shù)組,原列表不變",b)
打印如下
1 (list數(shù)組首部:,1) (獲取除了第一個(gè)元素外其他元素組成的列表,ListBuffer(2, 3, 4)) (添加應(yīng)元素,ListBuffer(1, 2, 3, 4, 5)) (添加一個(gè)不可變列表,ListBuffer(1, 2, 3, 4, 5, 6, 7)) (添加一個(gè)可變列表,ListBuffer(1, 2, 3, 4, 5, 6, 7, 8, 9)) (刪除單個(gè)元素,ListBuffer(1, 2, 3, 4, 5, 6, 7, 8)) (刪除一個(gè)不可變的列表存在的元素,ListBuffer(1, 2, 3, 4, 5, 6)) (刪除一個(gè)可變的列表存在的元素,ListBuffer(1, 2, 3, 4)) (toList根據(jù)可變的列表生成一個(gè)不可變列表,List(1, 2, 3, 4)) (toList根據(jù)可變的列表生成一個(gè)不可變列表,原列表不變,ListBuffer(1, 2, 3, 4)) (toArray根據(jù)可變的列表生成一個(gè)新的不可變數(shù)組,[I@3567135c) (toArray根據(jù)可變的列表生成一個(gè)新的不可變數(shù)組,原列表不變,ListBuffer(1, 2, 3, 4))
12、函數(shù)式編程
我們將來使用Spark/Flink的大量業(yè)務(wù)代碼都會(huì)使用到函數(shù)式編程。
下面的這些操作是學(xué)習(xí)的重點(diǎn),先來感受下如何進(jìn)行函數(shù)式編程以及它的強(qiáng)大
(1)、遍歷 - foreach
方法描述
foreach(f: (A) ? Unit): Unit
foreach | API | 說明 |
---|---|---|
參數(shù) | f: (A) ⇒ Unit | 接收一個(gè)函數(shù)對(duì)象作為參數(shù) 函數(shù)的輸入?yún)?shù)為集合的元素 返回值為空 |
返回值 | Unit | 空 |
def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4) //定義一個(gè)匿名函數(shù)傳入到foreach方法中 list.foreach((u: Int) => println(u)) //匿名函數(shù)的輸入?yún)?shù)類型可以省略,由編譯器自動(dòng)推斷 list.foreach(u => println(u)) // 當(dāng)函數(shù)參數(shù),只在函數(shù)體中出現(xiàn)一次,而且函數(shù)體沒有嵌套調(diào)用時(shí),可以使用下劃線來簡(jiǎn)化函數(shù)定義 list.foreach(println(_)) //最簡(jiǎn)單直接 list.foreach(println) }
(2)、映射 - map
集合的映射操作是將來在編寫Spark/Flink用得最多的操作,是我們必須要掌握。
方法描述
def map[B](f: (A) ? B): TraversableOnce[B]
方法說明
ap方法 | API | 說明 |
---|---|---|
泛型 | [B] | 指定map方法最終返回的集合泛型 |
參數(shù) | f: (A) ⇒ B | 傳入一個(gè)函數(shù)對(duì)象作為參數(shù) 該函數(shù)接收一個(gè)類型A(要轉(zhuǎn)換的集合的元素類型) 返回值為類型B |
返回值 | TraversableOnce[B] | B類型的集合 |
val list = List(1, 2, 3, 4) //定義一個(gè)匿名函數(shù) val b=list.map((i:Int)=>i*10) println(b) //省略匿名函數(shù)參數(shù)類型 val c=list.map(i=>i*10) println(c) //最簡(jiǎn)單用下劃線的方法 val d= list.map(_ * 10) println(d)
打印結(jié)果如下
List(10, 20, 30, 40) List(10, 20, 30, 40) List(10, 20, 30, 40)
(3)、扁平化映射 - flatmap
映射扁平化也是將來用得非常多的操作,也是必須要掌握的。
方法描述
def flatMap[B](f: (A) ? GenTraversableOnce[B]): TraversableOnce[B]
方法說明
flatmap方法 | API | 說明 |
---|---|---|
泛型 | [B] | 最終要轉(zhuǎn)換的集合元素類型 |
參數(shù) | f: (A) ⇒ GenTraversableOnce[B] | 傳入一個(gè)函數(shù)對(duì)象作為參數(shù) 函數(shù)的參數(shù)是集合的元素 函數(shù)的返回值是一個(gè)集合 |
返回值 | TraversableOnce[B] | B類型的集合 |
def main(args: Array[String]): Unit = { val list = List("hadoop hive spark flink", "hbase spark") val tt = list.flatMap(x => x.split(" ")); println(tt) //簡(jiǎn)寫 val t2 = list.flatMap(_.split(" ")) println(t2) //flatMap該方法其本質(zhì)是先進(jìn)行了map 然后又調(diào)用了flatten val t3 = list.map(_.split(" ")).flatten println(t3) }
打印結(jié)果如下
List(hadoop, hive, spark, flink, hbase, spark) List(hadoop, hive, spark, flink, hbase, spark) List(hadoop, hive, spark, flink, hbase, spark)
(4)、過濾 - filter
過濾符合一定條件的元素
方法描述
def filter(p: (A) ? Boolean): TraversableOnce[A]
方法說明
filter方法 | API | 說明 |
---|---|---|
參數(shù) | p: (A) ⇒ Boolean | 傳入一個(gè)函數(shù)對(duì)象作為參數(shù) 函數(shù)的參數(shù)是集合中的元素 此函數(shù)返回布爾類型,滿足條件返回true, 不滿足返回false |
返回值 | TraversableOnce[A] | 列表 |
demo展示
def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) ///過濾出集合中大于5的元素 val tt=list.filter(x=>x>5) println(tt) //把集合中大于5的元素取出來乘以10生成一個(gè)新的list集合 val t3=list.filter(x=>x>5).map(u=>u*10) println(t3) }
打印的值為:
List(6, 7, 8, 9, 10) List(60, 70, 80, 90, 100)
(5) 、分組 - groupBy
首先集合的元素得是kv對(duì)的;
如果要將數(shù)據(jù)按照某值分組來進(jìn)行統(tǒng)計(jì)分析,就需要使用到分組方法
groupBy表示按照函數(shù)將列表分成不同的組
方法描述
def groupBy[K](f: (A) ⇒ K): Map[K, List[A]]
方法說明
groupBy方法 | API | 說明 |
---|---|---|
泛型 | [K] | 分組字段的類型 |
參數(shù) | f: (A) ⇒ K | 傳入一個(gè)函數(shù)對(duì)象 接收集合元素類型的參數(shù) 返回一個(gè)K類型的key,這個(gè)key會(huì)用來進(jìn)行分組,相同的key放在一組中 |
返回值 | Map[K, List[A]] | 返回一個(gè)映射,K為分組字段,List為這個(gè)分組字段對(duì)應(yīng)的一組數(shù)據(jù) |
scala> val a = List("張三"->"男", "李四"->"女", "王五"->"男")
a: List[(String, String)] = List((張三,男), (李四,女), (王五,男))// 按照性別分組
scala> a.groupBy(_._2)
res0: scala.collection.immutable.Map[String,List[(String, String)]] = Map(男 -> List((張三,男), (王五,男)),
女 -> List((李四,女)))// 將分組后的映射轉(zhuǎn)換為性別/人數(shù)元組列表
scala> res0.map(x => x._1 -> x._2.size)
res3: scala.collection.immutable.Map[String,Int] = Map(男 -> 2, 女 -> 1)//根據(jù)這個(gè)例子,思考下,作業(yè)中如何使用scala編程,實(shí)現(xiàn)詞頻統(tǒng)計(jì)?
//求每個(gè)省份有多少人?
val b = List("張三"->("男", "北京"), "李四"->("女", "河北"), "王五"->("男", "北京"))scala> b.groupBy(_._2._2).map(x => (x._1, x._2.size))
res14: scala.collection.immutable.Map[String,Int] = Map(北京 -> 2, 河北 -> 1)
object yyy { def main(args: Array[String]): Unit = { val a = List("張三"->"男", "李四"->"女", "王五"->"男") println(a.groupBy(_._2)) val list2 =List("a,b,c","y,c,a,e,a,c,a") var map=list2.flatMap(x=>x.split(",")).groupBy(x=>x).map(y=>(y._1,y._2.size)); println(map) println("a的個(gè)數(shù)是:"+map("a")) } }
打印的值為
Map(男 -> List((張三,男), (王五,男)), 女 -> List((李四,女)))
Map(e -> 1, y -> 1, a -> 4, b -> 1, c -> 3)
a的個(gè)數(shù)是:4
(6)、排序 - sort
在scala集合中,可以使用以下幾種方式來進(jìn)行排序
sorted默認(rèn)排序
sortBy指定字段排序
sortWith自定義排序
sorted默認(rèn)排序
def main(args: Array[String]): Unit = { val list = List(5, 1, 2, 4, 3) println(list.sorted) }
List(1, 2, 3, 4, 5)
sortBy指定字段排序
def sortBy[B](f: (A) ? B): List[A]
sortBy方法 | API | 說明 |
---|---|---|
泛型 | [B] | 按照什么類型來進(jìn)行排序 |
參數(shù) | f: (A) ⇒ B | 傳入函數(shù)對(duì)象作為參數(shù) 函數(shù)接收一個(gè)集合類型的元素為參數(shù) 返回B類型的元素進(jìn)行排序 |
返回值 | List[A] | 返回排序后的列表 |
結(jié)果如下:
val list2 = List("1 hadoop", "2 spark", "3 flink") println(list2.sortBy(x=>x.split(" ")(0)))
打印的結(jié)果如下:
List(1 hadoop, 2 spark, 3 flink)
sortWith自定義排序
自定義排序,根據(jù)一個(gè)函數(shù)來進(jìn)行自定義排序
方法描述
def sortWith(lt: (A, A) ? Boolean): List[A]
sortWith方法 | API | 說明 |
---|---|---|
參數(shù) | lt: (A, A) ⇒ Boolean | 傳入一個(gè)比較大小的函數(shù)對(duì)象作為參數(shù) 函數(shù)接收兩個(gè)集合類型的元素作為參數(shù) 返回兩個(gè)元素大小,小于返回true,大于返回false |
返回值 | List[A] | 返回排序后的列表 |
val list3 = List(2, 3, 1, 6, 4, 5) //降序 var tt = list3.sortWith((x, y) => x > y) println(tt) //升序 var tt2 = list3.sortWith((x, y) => x < y) println(tt2) //簡(jiǎn)寫 var tt3 =list3.sortWith(_ > _) println(tt3)
打印的結(jié)果:
List(6, 5, 4, 3, 2, 1) List(1, 2, 3, 4, 5, 6) List(6, 5, 4, 3, 2, 1)
(5)、聚合 - reduce
reduce表示將列表,傳入一個(gè)函數(shù)進(jìn)行聚合計(jì)算
方法描述
def reduce[A1 >: A](op: (A1, A1) ? A1): A1
reduce方法 | API | 說明 |
---|---|---|
泛型 | [A1 >: A] | (下界)A1必須是集合元素類型的子類 |
參數(shù) | op: (A1, A1) ⇒ A1 | 傳入函數(shù)對(duì)象,用來不斷進(jìn)行聚合操作 第一個(gè)A1類型參數(shù)為:當(dāng)前聚合后的變量 第二個(gè)A1類型參數(shù)為:當(dāng)前要進(jìn)行聚合的元素 |
返回值 | A1 | 列表最終聚合為一個(gè)元素 |
def main(args: Array[String]): Unit = { val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) //基礎(chǔ)的寫法 var b = a.reduce((x, y) => x + y) println("基礎(chǔ)的寫法"+b) //簡(jiǎn)單的寫法 var c = a.reduce(_ + _) println("簡(jiǎn)單的寫法"+c) //從左往右計(jì)算 var d = a.reduceLeft(_ + _) println("從左往右計(jì)算"+d) //從右往左計(jì)算 var f = a.reduceRight(_ + _) println("從右往左計(jì)算"+f) }
基礎(chǔ)的寫法55 簡(jiǎn)單的寫法55 從左往右計(jì)算55 從右往左計(jì)算55
(6)、折疊 - fold
fold與reduce很像,但是多了一個(gè)指定初始值參數(shù)
def fold[A1 >: A](z: A1)(op: (A1, A1) ? A1): A1
reduce方法 | API | 說明 |
---|---|---|
泛型 | [A1 >: A] | (下界)A1必須是集合元素類型的子類 |
參數(shù)1 | z: A1 | 初始值 |
參數(shù)2 | op: (A1, A1) ⇒ A1 | 傳入函數(shù)對(duì)象,用來不斷進(jìn)行折疊操作 第一個(gè)A1類型參數(shù)為:當(dāng)前折疊后的變量 第二個(gè)A1類型參數(shù)為:當(dāng)前要進(jìn)行折疊的元素 |
返回值 | A1 | 列表最終折疊為一個(gè)元素 |
def main(args: Array[String]): Unit = { val a = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) var e = a.fold(0)(_ + _) println("簡(jiǎn)單折疊:",e) var g = a.fold(5)(_ + _) println("給定一個(gè)初始值,,折疊求和:"+g) var h= a.foldLeft(10)(_ + _) println("從左往右折疊:",h) }
(簡(jiǎn)單折疊:,55) 給定一個(gè)初始值,,折疊求和:60 (從左往右折疊:,65)
13、高階函數(shù)
使用函數(shù)值作為參數(shù),或者返回值為函數(shù)值的“函數(shù)”和“方法”,均稱之為“高階函數(shù)”。
1、函數(shù)值作為參數(shù)
def main(args: Array[String]): Unit = { //定義一個(gè)數(shù)組 val array = Array(1, 2, 3, 4, 5) //定義一個(gè)函數(shù) val func = (x: Int) => x * 10 //函數(shù)作為參數(shù)傳遞到方法中 val tt= array.map(func) println("通過map轉(zhuǎn)化的新數(shù)組:",tt) println("通過map轉(zhuǎn)化的新數(shù)組最大:",tt.max) }
打印的值為:
(通過map轉(zhuǎn)化的新數(shù)組:,[I@17550481) (通過map轉(zhuǎn)化的新數(shù)組:,50)
2、匿名函數(shù)
一個(gè)沒有名稱的函數(shù)----匿名函數(shù)
def main(args: Array[String]): Unit = { val array = Array(1, 2, 3, 4, 5) var a = array.map(x => x * 10) println(a.max) }
打印的值為:
50
3、柯里化
方法可以定義多個(gè)參數(shù)列表,當(dāng)使用較少的參數(shù)列表調(diào)用多參數(shù)列表的方法時(shí),會(huì)產(chǎn)生一個(gè)新的函數(shù),該函數(shù)接收剩余的參數(shù)列表作為其參數(shù)。這被稱為柯里化。
def main(args: Array[String]): Unit = { def getAddress(a: String): (String, String) => String = { (b: String,c: String) => a + "-" + b + "-" + c } var b=getAddress("china"); var c=b("beijing","tianAnMen") println(c) }
china-beijing-tianAnMen
4、閉包
函數(shù)里面引用外面類成員變量叫作閉包
def main(args: Array[String]): Unit = { var factor = 1 val f1 = (x: Int) => x * factor println(f1(8)) factor=2 println(f1(8)) }
8 16
//定義的函數(shù)f1,它的返回值是依賴于不在函數(shù)作用域的一個(gè)變量
//后期必須要要獲取到這個(gè)變量才能執(zhí)行
//spark和flink程序的開發(fā)中大量的使用到函數(shù),函數(shù)的返回值依賴的變量可能都需要進(jìn)行大量的網(wǎng)絡(luò)傳輸獲取得到。這里就需要這些變量實(shí)現(xiàn)序列化進(jìn)行網(wǎng)絡(luò)傳輸。
def main(args: Array[String]): Unit = { def multiply(x: Double) = (y: Double) => x * y //先進(jìn)行科爾化 val doubleFunc = multiply(2) val tripleFunc = multiply(3) // 再對(duì)閉包進(jìn)行計(jì)算 var u = doubleFunc(10) var u2 = tripleFunc(10) println(u) println(u2) }
20.0 30.0
14、scala面向?qū)ο缶幊讨?/h2>
1、類的定義
scala是支持面向?qū)ο蟮模灿蓄惡蛯?duì)象的概念。
定義一個(gè)Customer類,并添加成員變量/成員方法
添加一個(gè)main方法,并創(chuàng)建Customer類的對(duì)象,并給對(duì)象賦值,打印對(duì)象中的成員,調(diào)用成員方法
與java相比,他無需get set方法,就可以對(duì)變量進(jìn)行賦值封裝操作
import java.util.Date class Customer { var name: String = _ var sex: String = _ val registerDate: Date = new Date def sayHi(msg: String) = { println(msg) } } object Main { def main(args: Array[String]): Unit = { val customer = new Customer //給對(duì)象的成員變量賦值 customer.name = "張三" customer.sex = "男" println(s"姓名: ${customer.name}, 性別:${customer.sex}, 注冊(cè)時(shí)間: ${customer.registerDate}") //對(duì)象調(diào)用方法 customer.sayHi("你好!") } }
打印的結(jié)果為:
姓名: 張三, 性別:男, 注冊(cè)時(shí)間: Fri Apr 22 16:23:21 CST 2022 你好!
(1). var name:String = _, _表示使用默認(rèn)值進(jìn)行初始化
例如:String類型默認(rèn)值是null,Int類型默認(rèn)值是0,Boolean類型默認(rèn)值是false...
(2). val變量不能使用_來進(jìn)行初始化,因?yàn)関al是不可變的,所以必須手動(dòng)指定一個(gè)默認(rèn)值
(3). main方法必須要放在一個(gè)scala的object(單例對(duì)象)中才能執(zhí)行
2、類的構(gòu)造器
主構(gòu)造器
主構(gòu)造器是指在類名的后面跟上一系列參數(shù),例如
class 類名(var/val 參數(shù)名: 類型 = 默認(rèn)值, var/val 參數(shù)名: 類型 = 默認(rèn)值){ // 構(gòu)造代碼塊 }
輔助構(gòu)造器
在類中使用this來定義,例如
def this(參數(shù)名: 類型, 參數(shù)名: 類型) { ... }
package com.testScala.D_lei class Student(val name: String, val age: Int) { val address: String = "beijing" // 定義一個(gè)參數(shù)的輔助構(gòu)造器 def this(name: String) { // 輔助構(gòu)造器的第一行必須調(diào)用主構(gòu)造器或其他輔助構(gòu)造器或者super父類的構(gòu)造器 this(name, 20) } def this(age: Int) { this("某某某", age) } } object test { def main(args: Array[String]): Unit = { val tt = new Student("張三"); println(s"name等于:${tt.name},address:${tt.address},age:${tt.age}") } }
15、scala面向?qū)ο缶幊讨畬?duì)象
1、scala中的object
scala中是沒有Java中的靜態(tài)成員的。如果將來我們需要用到static變量、static方法,就要用到scala中的單例對(duì)象object
定義object
定義單例對(duì)象和定義類很像,就是把class換成object
演示
定義一個(gè)工具類,用來格式化日期時(shí)間
object DateUtils { // / 在object中定義的成員變量,相當(dāng)于Java中定義一個(gè)靜態(tài)變量 // 定義一個(gè)SimpleDateFormat日期時(shí)間格式化對(duì)象 val simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm") // 構(gòu)造代碼 println("構(gòu)造代碼") // 相當(dāng)于Java中定義一個(gè)靜態(tài)方法 def format(date: Date) = simpleDateFormat.format(date) // main是一個(gè)靜態(tài)方法,所以必須要寫在object中 def main(args: Array[String]): Unit = { println { DateUtils.format(new Date()) }; } }
(1). 使用object 單例對(duì)象名定義一個(gè)單例對(duì)象,可以用object作為工具類或者存放常量
(2). 在單例對(duì)象中定義的變量,類似于Java中的static成員變量
(3). 在單例對(duì)象中定義的方法,類似于Java中的static方法
(4). object單例對(duì)象的構(gòu)造代碼可以直接寫在花括號(hào)中
(5). 調(diào)用單例對(duì)象的方法,直接使用單例對(duì)象名.方法名,訪問單例對(duì)象的成員變量也是使用單例對(duì)象名.變量名
(6). 單例對(duì)象只能有一個(gè)無參的主構(gòu)造器,不能添加其他參數(shù)
2、scala中的伴生對(duì)象
在==同一個(gè)scala文件,有一個(gè)class和object具有同樣的名字==,那么就稱這個(gè)object是class的伴生對(duì)象,class是object的伴生類;
伴生類和伴生對(duì)象的最大特點(diǎn)是,可以相互訪問;
package com.testScala.E_duixiang class Dog { val id = 1 private var name = "kkb" def printName(): Unit = { //在Dog類中可以訪問伴生對(duì)象Dog的私有屬性 println(Dog.CONSTANT + name ) } } object Dog{ //伴生對(duì)象中的私有屬性 private val CONSTANT = "汪汪汪 : " def main(args: Array[String]) { val dog = new Dog //訪問私有的字段name dog.name = "123" dog.printName() } }
汪汪汪 : 123
(1). 伴生類和伴生對(duì)象的名字必須是一樣的
(2). 伴生類和伴生對(duì)象需要在一個(gè)scala源文件中
(3). 伴生類和伴生對(duì)象可以互相訪問private的屬性
3、scala中object的apply方法
:==實(shí)現(xiàn)伴生對(duì)象Array的apply方法==
伴生對(duì)象的apply方法用來快速地創(chuàng)建一個(gè)伴生類的對(duì)象。
package com.testScala.E_duixiang class Person(var name: String, var age: Int) { override def toString = s"Person($name, $age)" } object Person { // 實(shí)現(xiàn)apply方法 // 返回的是伴生類的對(duì)象 def apply(name: String, age: Int): Person = new Person(name, age) // apply方法支持重載 def apply(name: String): Person = new Person(name, 20) def apply(age: Int): Person = new Person("某某某", age) def apply(): Person = new Person("某某某", 20) } object Main2 { def main(args: Array[String]): Unit = { val p1 = Person("張三", 20) val p2 = Person("李四") val p3 = Person(100) val p4 = Person() println(p1) println(p2) println(p3) println(p4) } }
Person(張三, 20) Person(李四, 20) Person(某某某, 100) Person(某某某, 20)
(1). 當(dāng)遇到類名(參數(shù)1, 參數(shù)2...)會(huì)自動(dòng)調(diào)用伴生對(duì)象相應(yīng)的apply方法,在apply方法中來創(chuàng)建對(duì)象
(2). 定義apply時(shí),如果參數(shù)列表是空,也不能省略括號(hào)(),否則引用的是伴生對(duì)象
4、scala中object的main方法
scala和Java一樣,如果要運(yùn)行一個(gè)程序,必須有一個(gè)main方法。
而在Java中main方法是靜態(tài)的,而在scala中沒有靜態(tài)方法。
object Main1{ def main(args: Array[String]) = { println("hello, scala") } }
16、scala面向?qū)ο缶幊讨^承
1、繼承extends
scala和Java一樣,使用extends關(guān)鍵字來實(shí)現(xiàn)繼承??梢栽谧宇愔卸x父類中沒有的字段和方法,或者重寫父類的方法。
package com.testScala.F_jicheng class Person1 { var name = "jianghaojie" def getName = this.name } class Student extends Person1 object main1 { def main(args: Array[String]): Unit = { val p1 = new Person1() val p2 = new Student() p2.name = "蔣皓潔" println(p1.getName) println(p2.getName) } }
jianghaojie 蔣皓潔
2、override和super
???如果子類要覆蓋父類中的一個(gè)非抽象方法,必須要使用override關(guān)鍵字
可以使用override關(guān)鍵字來重寫一個(gè)val字段
可以使用super關(guān)鍵字來訪問父類的成員
==示例1:class繼承class==
class Person3 { val name = "super" def getName = name } class Student3 extends Person3 { // 重寫val字段 override val name: String = "child" // 重寫getName方法 override def getName: String = "hello, " + super.getName } object Main3 { def main(args: Array[String]): Unit = { val p = new Student3(); println(p.name) println(p.getName) } }
child hello, child
3、isInstanceOf和asInstanceOf
我們經(jīng)常要在代碼中進(jìn)行類型的判斷和類型的轉(zhuǎn)換。在Java中,我們可以使用instanceof關(guān)鍵字、以及(類型)object來實(shí)現(xiàn),在scala中如何實(shí)現(xiàn)呢?
scala中對(duì)象提供==isInstanceOf==和==asInstanceOf==方法。
isInstanceOf判斷對(duì)象是否為指定類的對(duì)象
asInstanceOf將對(duì)象轉(zhuǎn)換為指定類型
Java | Scala | |
---|---|---|
判斷對(duì)象是否是C類型 | obj instanceof C | obj.isInstanceof[C] |
將對(duì)象強(qiáng)轉(zhuǎn)成C類型 | (C ) obj | obj.asInstanceof[C] |
獲取類型為T的class對(duì)象 | C.class | classOf[C] |
==示例==
class Person4 class Student4 extends Person4 object Main4 { def main(args: Array[String]): Unit = { val s1: Person4 = new Student4 // 判斷s1是否為Student4類型 if(s1.isInstanceOf[Student4]) { // 將s1轉(zhuǎn)換為Student3類型 val s2 = s1.asInstanceOf[Student4] println(s2) } } }
com.testScala.F_jicheng.Student4@ea4a92b
4、getClass和classOf
isInstanceOf 只能判斷出對(duì)象是否為指定類以及其子類的對(duì)象,而不能精確的判斷出,對(duì)象就是指定類的對(duì)象。如果要求精確地判斷出對(duì)象就是指定類的對(duì)象,那么就只能使用 getClass 和 classOf 。
對(duì)象.getClass可以精確獲取對(duì)象的類型
classOf[x]可以精確獲取類型
使用==操作符就可以直接比較
class Person5 class Student5 extends Person5 object Student5{ def main(args: Array[String]) { val p: Person5 = new Student5 //判斷p是否為Person5類的實(shí)例 println(p.isInstanceOf[Person5])//true //判斷p的類型是否為Person5類 println(p.getClass == classOf[Person5])//false //判斷p的類型是否為Student5類 println(p.getClass == classOf[Student5])//true } }
5、訪問修飾符
java中的訪問控制,同樣適用于scala,可以在成員前面添加private/protected關(guān)鍵字來控制成員的可見性。但在scala中,==沒有public關(guān)鍵字,任何沒有被標(biāo)為private或protected的成員都是公共的==。
==private[this]修飾符==
被修飾的成員只能在當(dāng)前類中被訪問。或者可以理解為:
只能通過this.來訪問
(在當(dāng)前類中訪問成員會(huì)自動(dòng)添加this.)
package com.testScala.F_jicheng class Person6 { // 只有在當(dāng)前對(duì)象中能夠訪問 private[this] var name = "super" def getName = this.name // 正確! def sayHelloTo(p: Person6) = { println("hello" + p.name) // 報(bào)錯(cuò)!無法訪問 } } object Person6 { def showName(p: Person6) = println(p.name) // 報(bào)錯(cuò)!無法訪問 }
protected[this]修飾符==
==被修飾的成員只能在當(dāng)前類和當(dāng)前類的子類中被訪問==。也可以理解為:當(dāng)前類通過this.訪問或者子類通過this.訪問
示例
package com.testScala.F_jicheng class Person7 { // 只有在當(dāng)前類以及繼承該類的當(dāng)前對(duì)象中能夠訪問 protected[this] var name = "super" def getName = { // 正確! this.name } def sayHelloTo1(p: Person7) = { // 編譯錯(cuò)誤!無法訪問 // println(p.name) } } object Person7 { def sayHelloTo3(p: Person7) = { // 編譯錯(cuò)誤!無法訪問 // println(p.name) } } class Student7 extends Person7 { def showName = { // 正確! println(name) } def sayHelloTo2(p: Person7) = { // 編譯錯(cuò)誤!無法訪問 // println(p.name) } } object yy{ def main(args: Array[String]): Unit = { var yu= new Student7(); yu.showName // println() } }
6、調(diào)用父類的constructor
==實(shí)例化子類對(duì)象,必須要調(diào)用父類的構(gòu)造器==,在scala中,只能在子類的
主構(gòu)造器
中調(diào)用父類的構(gòu)造器示例
??????
package com.testScala.F_jicheng class Person8(var name: String){ println("name:"+name) } // 直接在子類的類名后面調(diào)用父類構(gòu)造器 class Student8(name: String, var clazz: String) extends Person8(name) object Main8 { def main(args: Array[String]): Unit = { val s1 = new Student8("張三", "三年二班") println(s"${s1.name} - ${s1.clazz}") } }
name:張三 張三 - 三年二班
7、抽象類
如果類的某個(gè)成員在當(dāng)前類中的定義是不包含完整的,它就是一個(gè)抽象類
不完整定義有兩種情況:
1.方法沒有方法體
2.變量沒有初始化
沒有方法體的方法稱為抽象方法,沒有初始化的變量稱為抽象字段。定義抽象類和Java一樣,在類前面加上abstract關(guān)鍵字就可以了
package com.testScala.F_jicheng abstract class Person9(val name: String) { //抽象方法 def sayHello: String def sayBye: String //抽象字段 val address: String } class Student9(name: String) extends Person9(name){ //重寫抽象方法或字段,def前不必加override關(guān)鍵字 def sayHello: String = "Hello," + name def sayBye: String ="Bye," + name //重寫抽象字段 val address: String = "beijing " } object Main9{ def main(args: Array[String]) { val s = new Student9("tom") println(s.sayHello) println(s.sayBye) println(s.address) } }
Hello,tom Bye,tom beijing
17、scala面向?qū)ο缶幊讨畉rait特質(zhì)
特質(zhì)是scala中代碼復(fù)用的基礎(chǔ)單元
它可以將方法和字段定義封裝起來,然后添加到類中
與類繼承不一樣的是,類繼承要求每個(gè)類都只能繼承
一個(gè)
超類,而一個(gè)類可以添加任意數(shù)量
的特質(zhì)。特質(zhì)的定義和抽象類的定義很像,但它是使用
trait
關(guān)鍵字
1、作為接口使用
使用
extends
來繼承trait(scala不論是類還是特質(zhì),都是使用extends關(guān)鍵字)如果要繼承多個(gè)trait,則使用
with
關(guān)鍵字
==示例一:繼承單個(gè)trait==
trait Logger1 { // 抽象方法 def log(msg: String) } class ConsoleLogger1 extends Logger1 { override def log(msg: String): Unit = println(msg) } object LoggerTrait1 { def main(args: Array[String]): Unit = { val logger = new ConsoleLogger1 logger.log("控制臺(tái)日志: 這是一條Log") } }
控制臺(tái)日志: 這是一條Log
==示例二:繼承多個(gè)trait==
trait Logger2 { // 抽象方法 def log(msg: String) } trait MessageSender { def send(msg: String) } class ConsoleLogger2 extends Logger2 with MessageSender { override def log(msg: String): Unit = println(msg) override def send(msg: String): Unit = println(s"發(fā)送消息:${msg}") } object LoggerTrait2 { def main(args: Array[String]): Unit = { val logger = new ConsoleLogger2 logger.log("控制臺(tái)日志: 這是一條Log") logger.send("你好!") } }
控制臺(tái)日志: 這是一條Log 發(fā)送消息:你好!
2、定義具體的方法
和類一樣,trait中還可以定義具體的方法。
==示例==
package com.testScala.G_trait trait LoggerDetail { // 在trait中定義具體方法 def log(msg: String) = println(msg) } class PersonService extends LoggerDetail { def add() = log("添加用戶") } object MethodInTrait { def main(args: Array[String]): Unit = { val personService = new PersonService personService.add() } }
添加用戶
3、定義具體方法和抽象方法
在trait中,可以混合使用具體方法和抽象方法
使用具體方法依賴于抽象方法,而抽象方法可以放到繼承trait的子類中實(shí)現(xiàn),這種設(shè)計(jì)方式也稱為模板模式
==示例==
package com.testScala.G_trait trait Logger3 { // 抽象方法 def log(msg: String) // 具體方法(該方法依賴于抽象方法log def info(msg: String) = log("INFO:" + msg) def warn(msg: String) = log("WARN:" + msg) def error(msg: String) = log("ERROR:" + msg) } class ConsoleLogger3 extends Logger3 { override def log(msg: String): Unit = println(msg) } object LoggerTrait3 { def main(args: Array[String]): Unit = { val logger3 = new ConsoleLogger3 logger3.log("這是一條日志信息") logger3.info("這是一條普通信息") logger3.warn("這是一條警告信息") logger3.error("這是一條錯(cuò)誤信息") } }
這是一條日志信息 INFO:這是一條普通信息 WARN:這是一條警告信息 ERROR:這是一條錯(cuò)誤信息
4、定義具體字段和抽象字段
在trait中可以定義具體字段和抽象字段
繼承trait的子類自動(dòng)擁有trait中定義的字段
字段直接被添加到子類中
package com.testScala.G_trait import java.text.SimpleDateFormat import java.util.Date trait LoggerEx { // 具體字段 val sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm") val INFO = "信息:" + sdf.format(new Date) // 抽象字段 val TYPE: String // 抽象方法 def log(msg: String) } class ConsoleLoggerEx extends LoggerEx { // 實(shí)現(xiàn)抽象字段 override val TYPE: String = "控制臺(tái)" // 實(shí)現(xiàn)抽象方法 override def log(msg: String): Unit = print(s"$TYPE$INFO $msg") } object FieldInTrait { def main(args: Array[String]): Unit = { val logger = new ConsoleLoggerEx logger.log("這是一條消息") } }
控制臺(tái)信息:2022-04-24 15:05 這是一條消息
5、實(shí)例對(duì)象混入trait
trait還可以混入到
實(shí)例對(duì)象
中,給對(duì)象實(shí)例添加額外的行為只有混入了trait的對(duì)象才具有trait中的方法,其他的類對(duì)象不具有trait中的行為
使用with將trait混入到實(shí)例對(duì)象中
==示例==
package com.testScala.G_trait trait LoggerMix { def log(msg: String) = println(msg) } class UserService object FixedInClass { def main(args: Array[String]): Unit = { // 使用with關(guān)鍵字直接將特質(zhì)混入到對(duì)象中 val userService = new UserService with LoggerMix userService.log("你好") } }
你好
18、模式匹配和樣例類
scala有一個(gè)十分強(qiáng)大的模式匹配機(jī)制,可以應(yīng)用到很多場(chǎng)合。
switch語句
類型查詢
以及快速獲取數(shù)據(jù)
并且scala還提供了樣例類,對(duì)模式匹配進(jìn)行了優(yōu)化,可以快速進(jìn)行匹配。
1、匹配字符串
import scala.util.Random //todo:匹配字符串 object CaseDemo01 extends App{ //定義一個(gè)數(shù)組 val arr = Array("hadoop", "zookeeper", "spark", "storm") //隨機(jī)取數(shù)組中的一位,使用Random.nextInt val name = arr(Random.nextInt(arr.length)) println(name) name match { case "hadoop" => println("大數(shù)據(jù)分布式存儲(chǔ)和計(jì)算框架...") case "zookeeper" => println("大數(shù)據(jù)分布式協(xié)調(diào)服務(wù)框架...") case "spark" => println("大數(shù)據(jù)分布式內(nèi)存計(jì)算框架...") //表示以上情況都不滿足才會(huì)走最后一個(gè) case _ => println("我不認(rèn)識(shí)你") } }
hadoop 大數(shù)據(jù)分布式存儲(chǔ)和計(jì)算框架...
2、匹配類型
//todo:匹配類型 object CaseDemo02 extends App{ //定義一個(gè)數(shù)組 val arr = Array("hello", 1, -2.0, CaseDemo02) //隨機(jī)獲取數(shù)組中的元素 val value = arr(Random.nextInt(arr.length)) println(value) value match { case x: Int => println("Int=>"+x) case y: Double if(y >= 0) => println("Double=>"+y) case z: String => println("String=>"+z) case _ => throw new Exception("not match exception") } }
hello String=>hello
3、匹配數(shù)組
package com.testScala.H_pipei //匹配數(shù)組 object CaseDemo03 extends App{ //匹配數(shù)組 val arr = Array(1, 3, 5) arr match { case Array(1, x, y) => println(x + "---" + y) case Array(1, _*) => println("1...") case Array(0) => println("only 0") case _ => println("something else") } }
1...
4、匹配集合
//匹配集合 object CaseDemo04 extends App{ val list = List(0, 3, 6) list match { case 0::Nil => println("only 0") case 0::tail => println("0....") case x::y::z::Nil => println(s"x:$x y:$y z:$z") case _ => println("something else") } }
0....
5、匹配元組
package com.testScala.H_pipei //匹配元組 object CaseDemo05 extends App{ val tuple = (1, 3, 5) tuple match { case (1, x, y) => println(s"1,$x,$y") case (2, x, y) => println(s"$x,$y") case _ => println("others...") } }
1,3,5
6、樣例類
樣例類是一種特殊類,它可以用來快速定義一個(gè)用于保存數(shù)據(jù)的類(類似于Java POJO類),==而且它會(huì)自動(dòng)生成apply方法,允許我們快速地創(chuàng)建樣例類實(shí)例對(duì)象==。后面在并發(fā)編程和spark、flink這些框架也都會(huì)經(jīng)常使用它。
定義樣例類
語法結(jié)構(gòu)
case class 樣例類名(成員變量名1: 類型1, 成員變量名2: 類型2 ...)
package com.testScala.H_pipei // 定義一個(gè)樣例類 // 樣例類有兩個(gè)成員name、age case class CasePerson(name: String, age: Int) // 使用var指定成員變量是可變的 case class CaseStudent(var name: String, var age: Int) object CaseClassDemo { def main(args: Array[String]): Unit = { // 1. 使用new創(chuàng)建實(shí)例 val zhagnsan = new CasePerson("張三", 20) println(zhagnsan) // 2. 使用類名直接創(chuàng)建實(shí)例 val lisi = CasePerson("李四", 21) println(lisi) // 3. 樣例類默認(rèn)的成員變量都是val的,除非手動(dòng)指定變量為var類型 //lisi.age = 22 // 編譯錯(cuò)誤!age默認(rèn)為val類型 val xiaohong = CaseStudent("小紅", 23) xiaohong.age = 24 println(xiaohong) } }
CasePerson(張三,20) CasePerson(李四,21) CaseStudent(小紅,24)
樣例對(duì)象
使用case object可以創(chuàng)建樣例對(duì)象。樣例對(duì)象是單例的,而且它沒有主構(gòu)造器。樣例對(duì)象是可序列化的。格式:
case object 樣例對(duì)象名
case class SendMessage(text: String) // 消息如果沒有任何參數(shù),就可以定義為樣例對(duì)象 case object startTask case object PauseTask case object StopTask
case class SubmitTask(id: String, name: String) case class HeartBeat(time: Long) case object CheckTimeOutTask object CaseDemo06 extends App{ val arr = Array(CheckTimeOutTask, HeartBeat(10000), SubmitTask("0001", "task-0001")) arr(Random.nextInt(arr.length)) match { case SubmitTask(id, name) => println(s"id=$id, name=$name") case HeartBeat(time) => println(s"time=$time") case CheckTimeOutTask => println("檢查超時(shí)") } }
檢查超時(shí)
7、Option類型
在Scala中Option類型用樣例類來表示可能存在或也可能不存在的值
Option類型有2個(gè)子類
一個(gè)是Some
Some包裝了某個(gè)值
一個(gè)是None
None表示沒有值
object TestOption { def main(args: Array[String]) { val map = Map("a" -> 1, "b" -> 2) val value: Option[Int] = map.get("b") println(value) val v1 = value match { case Some(i) => i case None => 0 } println(v1) //更好的方式 val v2 = map.getOrElse("c", 0) println(v2) } }
Some(2) 2 0
8、偏函數(shù)
被包在花括號(hào)內(nèi)==沒有match的一組case語句==是一個(gè)偏函數(shù)
它是PartialFunction[A, B]的一個(gè)實(shí)例,
A代表輸入?yún)?shù)類型
B代表返回結(jié)果類型
可以理解為:偏函數(shù)是一個(gè)參數(shù)和一個(gè)返回值的函數(shù)。
==示例==
object TestPartialFunction { // func1是一個(gè)輸入?yún)?shù)為Int類型,返回值為String類型的偏函數(shù) val func1: PartialFunction[Int, String] = { case 1 => "一個(gè)" case 2 => "二個(gè)" case 3 => "三個(gè)" case _ => "其他" } def main(args: Array[String]): Unit = { println(func1(1)) val list = List(1, 2, 3, 4, 5, 6) //使用偏函數(shù)操作 val result = list.filter{ case x if x >3 => true case _ => false } println(result) }
19、異常處理
??????
def main(args: Array[String]): Unit = { val i = 10 / 0 println("你好!") } Exception in thread "main" java.lang.ArithmeticException: / by zero at ForDemo$.main(ForDemo.scala:3) at ForDemo.main(ForDemo.scala)
執(zhí)行程序,可以看到scala拋出了異常,而且沒有打印出來"你好"。說明程序出現(xiàn)錯(cuò)誤后就終止了。那怎么解決該問題呢?
1、 捕獲異常
在scala中,可以使用異常處理來解決這個(gè)問題。
在Scala里,借用了==模式匹配的思想來做異常的匹配==
以下為scala中try...catch異常處理的語法格式:
try {
// 代碼
}
catch {
case ex: 異常類型1 => // 代碼
case ex: 異常類型2 => // 代碼
}
finally {
// 代碼
}
try中的代碼是我們編寫的業(yè)務(wù)處理代碼
在catch中表示當(dāng)出現(xiàn)某個(gè)異常時(shí),需要執(zhí)行的代碼
在finally中,是不管是否出現(xiàn)異常都會(huì)執(zhí)行的代碼
try {
val i = 10 / 0} catch {
case ex: Exception => println(ex.getMessage)
} finally {
println("我始終都會(huì)執(zhí)行!")
}
2、拋出異常
我們也可以在一個(gè)方法中,拋出異常。語法格式和Java類似,使用throw new Exception...
==示例==
def main(args: Array[String]): Unit = { throw new Exception("這是一個(gè)異常") } Exception in thread "main" java.lang.Exception: 這是一個(gè)異常 at ForDemo$.main(ForDemo.scala:3) at ForDemo.main(ForDemo.scala)
20、提取器(Extractor)
=提取器是從傳遞給它的對(duì)象中提取出構(gòu)造該對(duì)象的參數(shù)==。(回想樣例類進(jìn)行模式匹配提取參數(shù))
scala 提取器是一個(gè)帶有unapply方法的對(duì)象。
==unapply方法算是apply方法的反向操作==
unapply接受一個(gè)對(duì)象,然后從對(duì)象中提取值,提取的值通常是用來構(gòu)造該對(duì)象的值。
package com.testScala.I_Extractor class Student { var name: String = _ // 姓名 var age: Int = _ // 年齡 // 實(shí)現(xiàn)一個(gè)輔助構(gòu)造器 def this(name: String, age: Int) = { this() this.name = name this.age = age } } object Student { def apply(name: String, age: Int): Student = new Student(name, age) // 實(shí)現(xiàn)一個(gè)解構(gòu)器 def unapply(arg: Student): Option[(String, Int)] = Some(arg.name, arg.age) } object extractor_DEMO { def main(args: Array[String]): Unit = { val zhangsan = Student("張三", 20) zhangsan match { case Student(name, age) => println(s"姓名:$name 年齡:$age") case _ => println("未匹配") } } }
姓名:張三 年齡:20
21、泛型
scala和Java一樣,類和特質(zhì)、方法都可以支持泛型。我們?cè)趯W(xué)習(xí)集合的時(shí)候,一般都會(huì)涉及到泛型。
在scala中,使用方括號(hào)來定義==類型參數(shù)==。
1、定義一個(gè)泛型方法
object learn01 { def getMiddle[A](arr: Array[A]) = arr(arr.length / 2) def main(args: Array[String]): Unit = { val arr1 = Array(1, 2, 3, 4, 5) val arr2 = Array("a", "b", "c", "d", "f") println(getMiddle[Int](arr1)) println(getMiddle[String](arr2)) // 簡(jiǎn)寫方式,scala自動(dòng)進(jìn)行類型推斷 println(getMiddle(arr1)) println(getMiddle(arr2)) } }
2、定義一個(gè)泛型類
定義一個(gè)Pair類包含2個(gè)類型不固定的泛型
package com.testScala.J_fanxing // 類名后面的方括號(hào),就表示這個(gè)類可以使用兩個(gè)類型、分別是T和S // 這個(gè)名字可以任意取 class Pair[T, S](val first: T, val second: S) case class People(var name: String, val age: Int) object Pair { def main(args: Array[String]): Unit = { val p1 = new Pair[String, Int]("張三", 10) println(p1.first) val p2 = new Pair[String, String]("張三", "1988-02-19") println(p2.first) val p3 = new Pair[People, People](People("張三", 20), People("李四", 30)) println(p3.first.age) } }
張三 張三 20
22、上下界
==在指定泛型類型時(shí),有時(shí)需要界定泛型類型的范圍,而不是接收任意類型==。比如,要求某個(gè)泛型類型,必須是某個(gè)類的子類,這樣在程序中就可以放心的調(diào)用父類的方法,程序才能正常的使用與運(yùn)行.
scala的上下邊界特性允許泛型類型是某個(gè)類的子類,或者是某個(gè)類的父類
1、 ==U >: T==
這是類型==下界==的定義,也就是U必須是類型T的父類或者是T類型本身。
2、 ==U <: T==
這是類型==上界==的定義,也就是U必須是類型T的子類或者是T類型本身。
==示例一==
package com.testScala.J_fanxing // 類名后面的指定泛型的范圍 ----上界 class Pair1[T <: Person, S <: Person](val first: T, val second: S) { def chat(msg: String) = println(s"${first.name}對(duì)${second.name}說: $msg") } class Person(var name: String, val age: Int) object Pair1 { def main(args: Array[String]): Unit = { val p3 = new Pair1[Person, Person](new Person("張三", 20), new Person("李四", 30)) p3.chat("你好啊!") } }
張三對(duì)李四說: 你好??!
==示例二==
class Pair22[T <: Person, S >: Policeman <: Person](val first: T, val second: S) { def chat(msg: String) = println(s"${first.name}對(duì)${second.name}說: $msg") } class Person(var name: String, val age: Int) class Policeman(name: String, age: Int) extends Person(name, age) class Superman(name: String) extends Policeman(name, -1) object Pai { def main(args: Array[String]): Unit = { // 編譯錯(cuò)誤:第二個(gè)類型參數(shù)必須是Person的子類(包括本身)、Policeman的父類(包括本身) val p3 = new Pair22[Person, Policeman](new Person("張三", 20), new Policeman("李四",20)) p3.chat("你好啊!") } }
張三對(duì)李四說: 你好啊!
23、協(xié)變、逆變、非變
來一個(gè)類型轉(zhuǎn)換的問題
class Pair[T](a: T) object Pair { def main(args: Array[String]): Unit = { val p1 = new Pair("hello") // 編譯報(bào)錯(cuò),無法將p1轉(zhuǎn)換為p2 val p2: Pair[AnyRef] = p1 println(p2) } }
==協(xié)變==
class Pair[+T],這種情況是協(xié)變。類型B是A的子類型,Pair[B]可以認(rèn)為是Pair[A]的子類型。這種情況,參數(shù)化類型的方向和類型的方向是一致的。
==逆變==
class Pair[-T],這種情況是逆變。類型B是A的子類型,Pair[A]反過來可以認(rèn)為是Pair[B]的子類型。這種情況,參數(shù)化類型的方向和類型的方向是相反的。
==非變==
class Pair[T]{},這種情況就是非變(默認(rèn)),類型B是A的子類型,Pair[A]和Pair[B]沒有任何從屬關(guān)系,這種情況和Java是一樣的。
package com.testScala.K_xieBian class Super class Sub extends Super //非變 class Temp1[A](title: String) //協(xié)變 class Temp2[+A](title: String) //逆變 class Temp3[-A](title: String) object Covariance_demo { def main(args: Array[String]): Unit = { val a = new Sub() // 沒有問題,Sub是Super的子類 val b: Super = a // 非變 val t1: Temp1[Sub] = new Temp1[Sub]("測(cè)試") // 報(bào)錯(cuò)!默認(rèn)不允許轉(zhuǎn)換 // val t2: Temp1[Super] = t1 // 協(xié)變 val t3: Temp2[Sub] = new Temp2[Sub]("測(cè)試") val t4: Temp2[Super] = t3 // 逆變 val t5: Temp3[Super] = new Temp3[Super]("測(cè)試") val t6: Temp3[Sub] = t5 } }
==總結(jié)==
C[+T]:如果A是B的子類,那么C[A]是C[B]的子類。
C[-T]:如果A是B的子類,那么C[B]是C[A]的子類。
C[T]: 無論A和B是什么關(guān)系,C[A]和C[B]沒有從屬關(guān)系。
24、隱式轉(zhuǎn)換和隱式參數(shù)
1 隱式轉(zhuǎn)換
Scala提供的隱式轉(zhuǎn)換和隱式參數(shù)功能,是非常有特色的功能。是Java等編程語言所沒有的功能。
它可以允許你手動(dòng)指定,將某種類型的對(duì)象轉(zhuǎn)換成其他類型的對(duì)象或者是給一個(gè)類增加方法。通過這些功能,可以實(shí)現(xiàn)非常強(qiáng)大、特殊的功能。
隱式轉(zhuǎn)換其核心就是定義一個(gè)使用 ==implicit== 關(guān)鍵字修飾的方法 實(shí)現(xiàn)把一個(gè)原始類轉(zhuǎn)換成目標(biāo)類,進(jìn)而可以調(diào)用目標(biāo)類中的方法
.2 隱式參數(shù)
所謂的隱式參數(shù),指的是在函數(shù)或者方法中,定義一個(gè)用implicit修飾的參數(shù),此時(shí)Scala會(huì)嘗試找到一個(gè)指定類型的用implicit修飾的值,即==隱式值==,并注入?yún)?shù)。
==所有的隱式轉(zhuǎn)換和隱式參數(shù)必須定義在一個(gè)object中==
package com.kaikeba.implic_demo //todo:隱式轉(zhuǎn)換案例二:超人變身 class Man(val name: String) class SuperMan(val name: String) { def heat = print("超人打怪獸") } object SuperMan{ //隱式轉(zhuǎn)換方法 implicit def man2SuperMan(man: Man) = new SuperMan(man.name) def main(args: Array[String]) { val hero = new Man("hero") //Man具備了SuperMan的方法 hero.heat } }
到此這篇關(guān)于Scala基礎(chǔ)語法總結(jié)的文章就介紹到這了,更多相關(guān)Scala基礎(chǔ)語法詳解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Scala實(shí)現(xiàn)二分查找的代碼實(shí)例
這篇文章主要介紹了Scala實(shí)現(xiàn)二分查找的代碼實(shí)例,找到數(shù)組的中間值,和需要查找的值進(jìn)行對(duì)比:如果中間值等于查找值,直接返回中間值下標(biāo);如果中間值大于查找值,則遞歸向左邊查找;如果中間值小于查找值,則遞歸向右邊查找,直到找完所有的元素,需要的朋友可以參考下2023-11-11淺談Scala的Class、Object和Apply()方法
下面小編就為大家?guī)硪黄獪\談Scala的Class、Object和Apply()方法。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05利用Gradle如何構(gòu)建scala多模塊工程的步驟詳解
這篇文章主要給大家介紹了關(guān)于如何利用Gradle構(gòu)建scala多模塊工程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04