Scala 高級(jí)用法
樣例類
? 樣例類是一種特殊類,它可以用來(lái)快速定義一個(gè)用于保存數(shù)據(jù)的類(類似于Java POJO類)氓润,在后續(xù)要學(xué)習(xí)并發(fā)編程和spark、flink這些框架也都會(huì)經(jīng)常使用它牺丙。
語(yǔ)法
ase class 樣例類名([var/val] 成員變量名1:類型1, 成員變量名2:類型2, 成員變量名3:類型3)
示例:
需求
- 定義一個(gè)Person樣例類午绳,包含姓名和年齡成員變量
- 創(chuàng)建樣例類的對(duì)象實(shí)例("張三"、20)痒谴,并打印它
object _01CaseClassDemo {
case class Person(name:String, age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("張三", 20)
println(zhangsan)
}
}
需求
- 定義一個(gè)Person樣例類衰伯,包含姓名和年齡成員變量
- 創(chuàng)建樣例類的對(duì)象實(shí)例("張三"、20)
- 修改張三的年齡為23歲积蔚,并打印
object _02CaseClassDemo {
case class Person(var name:String, var age:Int)
def main(args: Array[String]): Unit = {
val zhangsan = Person("張三", 20)
zhangsan.age = 23
println(zhangsan)
}
}
樣例類的方法
當(dāng)我們定義一個(gè)樣例類,編譯器自動(dòng)幫助我們實(shí)現(xiàn)了以下幾個(gè)有用的方法:
- apply方法
- toString方法
- equals方法
- hashCode方法
- copy方法
apply方法
apply方法可以讓我們快速地使用類名來(lái)創(chuàng)建對(duì)象烦周。參考以下代碼:
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
}
}
默認(rèn)就是用apply方法
toString方法
toString返回樣例類名稱(成員變量1, 成員變量2, 成員變量3....)尽爆,我們可以更方面查看樣例類的成員
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
// 輸出:CasePerson(李四,21)
}
}
默認(rèn)也可以不用寫toString,直接打印類名直接就是toString的功能
equals方法
樣例類自動(dòng)實(shí)現(xiàn)了equals方法读慎,可以直接使用==比較兩個(gè)樣例類是否相等漱贱,即所有的成員變量是否相等
示例
- 創(chuàng)建一個(gè)樣例類Person,包含姓名夭委、年齡
- 創(chuàng)建名字年齡分別為"李四", 21的兩個(gè)對(duì)象
- 比較它們是否相等
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 21)
println(lisi1 == lisi2)
// 輸出:true
hashCode方法
樣例類自動(dòng)實(shí)現(xiàn)了hashCode方法幅狮,如果所有成員變量的值相同,則hash值相同株灸,只要有一個(gè)不一樣崇摄,則hash值不一樣。
示例
- 創(chuàng)建名字年齡分別為"李四", 21的對(duì)象
- 再創(chuàng)建一個(gè)名字年齡分別為"李四", 22的對(duì)象
- 分別打印這兩個(gè)對(duì)象的哈希值
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 22)
println(lisi1.hashCode())
println(lisi2.hashCode())
copy方法
樣例類實(shí)現(xiàn)了copy方法慌烧,可以快速創(chuàng)建一個(gè)相同的實(shí)例對(duì)象逐抑,可以使用帶名參數(shù)指定給成員進(jìn)行重新賦值
示例
- 創(chuàng)建名字年齡分別為"李四", 21的對(duì)象
- 通過(guò)copy拷貝,名字為"王五"的對(duì)象
**示例**
- 創(chuàng)建名字年齡分別為"李四", 21的對(duì)象
- 通過(guò)copy拷貝屹蚊,名字為"王五"的對(duì)象
樣例對(duì)象
語(yǔ)法
case object 樣例對(duì)象名
示例
需求說(shuō)明
- 定義一個(gè)性別Sex枚舉厕氨,它只有兩個(gè)實(shí)例(男性——Male进每、女性——Female)
- 創(chuàng)建一個(gè)Person類,它有兩個(gè)成員(姓名命斧、性別)
- 創(chuàng)建兩個(gè)Person對(duì)象("張三"田晚、男性)、("李四"国葬、"女")
trait Sex /*定義一個(gè)性別特質(zhì)*/
case object Male extends Sex // 定義一個(gè)樣例對(duì)象并實(shí)現(xiàn)了Sex特質(zhì)
case object Female extends Sex
case class Person(name:String, sex:Sex)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val zhangsan = Person("張三", Male)
println(zhangsan)
}
}
模式匹配
簡(jiǎn)單匹配模式
在Java中贤徒,有switch關(guān)鍵字,可以簡(jiǎn)化if條件判斷語(yǔ)句胃惜。在scala中泞莉,可以使用match表達(dá)式替代。
語(yǔ)法
變量 match {
case "常量1" => 表達(dá)式1
case "常量2" => 表達(dá)式2
case "常量3" => 表達(dá)式3
case _ => 表達(dá)式4 // 默認(rèn)匹配
}
示例
需求說(shuō)明
- 從控制臺(tái)輸入一個(gè)單詞(使用StdIn.readLine方法)
- 判斷該單詞是否能夠匹配以下單詞船殉,如果能匹配鲫趁,返回一句話
- 打印這句話
println("請(qǐng)輸出一個(gè)詞:")
// StdIn.readLine表示從控制臺(tái)讀取一行文本
val name = StdIn.readLine()
val result = name match {
case "hadoop" => "大數(shù)據(jù)分布式存儲(chǔ)和計(jì)算框架"
case "zookeeper" => "大數(shù)據(jù)分布式協(xié)調(diào)服務(wù)框架"
case "spark" => "大數(shù)據(jù)分布式內(nèi)存計(jì)算框架"
case _ => "未匹配"
}
println(result)
匹配類型
語(yǔ)法
變量 match {
case 類型1變量名: 類型1 => 表達(dá)式1
case 類型2變量名: 類型2 => 表達(dá)式2
case 類型3變量名: 類型3 => 表達(dá)式3
...
case _ => 表達(dá)式4
}
示例
需求說(shuō)明
- 定義一個(gè)變量為Any類型,然后分別給其賦值為"hadoop"利虫、1挨厚、1.0
- 定義模式匹配,然后分別打印類型的名稱
val a:Any = "hadoop"
val result = a match {
case _:String => "String"
case _:Int => "Int"
case _:Double => "Double"
}
println(result)
守衛(wèi)
就是java中case方法里的default糠惫,也就是說(shuō)在沒(méi)有匹配到值得情況下得取一個(gè)默認(rèn)的值
示例
需求說(shuō)明
- 從控制臺(tái)讀入一個(gè)數(shù)字a(使用StdIn.readInt)
- 如果 a >= 0 而且 a <= 3疫剃,打印[0-3]
- 如果 a >= 4 而且 a <= 8,打印[3,8]
- 否則硼讽,打印未匹配
val a = StdIn.readInt()
a match {
case _ if a >= 0 && a <= 3 => println("[0-3]")
case _ if a >= 4 && a <= 8 => println("[3-8]")
case _ => println("未匹配")
}
匹配樣例類
scala可以使用模式匹配來(lái)匹配樣例類巢价,從而可以快速獲取樣例類中的成員數(shù)據(jù)。后續(xù)固阁,我們?cè)陂_發(fā)Akka案例時(shí)壤躲,還會(huì)用到。
需求說(shuō)明
- 創(chuàng)建兩個(gè)樣例類Customer备燃、Order
- Customer包含姓名碉克、年齡字段
- Order包含id字段
- 分別定義兩個(gè)案例類的對(duì)象,并指定為Any類型
- 使用模式匹配這兩個(gè)對(duì)象并齐,并分別打印它們的成員變量值
/ 1. 創(chuàng)建兩個(gè)樣例類
case class Person(name:String, age:Int)
case class Order(id:String)
def main(args: Array[String]): Unit = {
// 2. 創(chuàng)建樣例類對(duì)象漏麦,并賦值為Any類型
val zhangsan:Any = Person("張三", 20)
val order1:Any = Order("001")
// 3. 使用match...case表達(dá)式來(lái)進(jìn)行模式匹配
// 獲取樣例類中成員變量
order1 match {
case Person(name, age) => println(s"姓名:${name} 年齡:${age}")
case Order(id1) => println(s"ID為:${id1}")
case _ => println("未匹配")
}
}
匹配集合,列表况褪,元祖
匹配集合
scala中的模式匹配撕贞,還能用來(lái)匹配集合。
示例
-
依次修改代碼定義以下三個(gè)數(shù)組
Array(1,x,y) // 以1開頭窝剖,后續(xù)的兩個(gè)元素不固定 Array(0) // 只匹配一個(gè)0元素的元素 Array(0, ...) // 可以任意數(shù)量麻掸,但是以0開頭
使用模式匹配上述數(shù)組
val arr = Array(1, 3, 5)
arr match {
case Array(1, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
case Array(0, _*) => println("0 ...")
case _ => println("something else")
}
匹配列表
示例
-
依次修改代碼定義以下三個(gè)列表
List(0) // 只保存0一個(gè)元素的列表 List(0,...) // 以0開頭的列表,數(shù)量不固定 List(x,y) // 只包含兩個(gè)元素的列表
使用模式匹配上述列表
val list = List(0, 1, 2)
list match {
case 0 :: Nil => println("只有0的列表")
case 0 :: tail => println("0開頭的列表")
case x :: y :: Nil => println(s"只有另兩個(gè)元素${x}, ${y}的列表")
case _ => println("未匹配")
}
匹配元祖
-
依次修改代碼定義以下兩個(gè)元組
(1, x, y) // 以1開頭的赐纱、一共三個(gè)元素的元組 (x, y, 5) // 一共有三個(gè)元素脊奋,最后一個(gè)元素為5的元組
使用模式匹配上述元素
val tuple = (2, 2, 5)
tuple match {
case (1, x, y) => println(s"三個(gè)元素熬北,1開頭的元組:1, ${x}, ${y}")
case (x, y, 5) => println(s"三個(gè)元素,5結(jié)尾的元組:${x}, ${y}, 5")
case _ => println("未匹配")
}
Option 類型
使用Option類型诚隙,可以用來(lái)有效避免空引用(null)異常讶隐。也就是說(shuō),將來(lái)我們返回某些數(shù)據(jù)時(shí)久又,可以返回一個(gè)Option類型來(lái)替代巫延。
Some(x):表示實(shí)際的值
None:表示沒(méi)有值
使用getOrElse方法,當(dāng)值為None是可以指定一個(gè)默認(rèn)值
示例說(shuō)明
- 定義一個(gè)兩個(gè)數(shù)相除的方法地消,使用Option類型來(lái)封裝結(jié)果
- 然后使用模式匹配來(lái)打印結(jié)果
- 不是除零炉峰,打印結(jié)果
- 除零打印異常錯(cuò)誤
/**
* 定義除法操作
* @param a 參數(shù)1
* @param b 參數(shù)2
* @return Option包裝Double類型
*/
def dvi(a:Double, b:Double):Option[Double] = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result1 = dvi(1.0, 5)
result1 match {
case Some(x) => println(x)
case None => println("除零異常")
}
}
示例說(shuō)明
- 重寫上述案例,使用getOrElse方法脉执,當(dāng)除零時(shí)疼阔,或者默認(rèn)值為0
def dvi(a:Double, b:Double) = {
if(b != 0) {
Some(a / b)
}
else {
None
}
}
def main(args: Array[String]): Unit = {
val result = dvi(1, 0).getOrElse(0)
println(result)
}
偏函數(shù)
偏函數(shù)可以提供了簡(jiǎn)潔的語(yǔ)法,可以簡(jiǎn)化函數(shù)的定義半夷。配合集合的函數(shù)式編程婆廊,可以讓代碼更加優(yōu)雅。
定義
偏函數(shù)被包在花括號(hào)內(nèi)沒(méi)有match的一組case語(yǔ)句是一個(gè)偏函數(shù)
-
偏函數(shù)是PartialFunction[A, B]的一個(gè)實(shí)例
- A代表輸入?yún)?shù)類型
- B代表返回結(jié)果類型
示例說(shuō)明*
定義一個(gè)偏函數(shù)巫橄,根據(jù)以下方式返回
1->一
2->二
3->三
其他 —> 其他
// func1是一個(gè)輸入?yún)?shù)為Int類型淘邻,返回值為String類型的偏函數(shù)
val func1: PartialFunction[Int, String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "其他"
}
println(func1(2))
示例說(shuō)明
- 定義一個(gè)列表,包含1-10的數(shù)字
- 請(qǐng)將1-3的數(shù)字都轉(zhuǎn)換為[1-3]
- 請(qǐng)將4-8的數(shù)字都轉(zhuǎn)換為[4-8]
- 將其他的數(shù)字轉(zhuǎn)換為(8-*]
val list = (1 to 10).toList
val list2 = list.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case x if x > 8 => "(8-*]"
}
println(list2)
正則表達(dá)式
Regex類
scala中提供了Regex類來(lái)定義正則表達(dá)式
要構(gòu)造一個(gè)RegEx對(duì)象湘换,直接使用String類的r方法即可
建議使用三個(gè)雙引號(hào)來(lái)表示正則表達(dá)式宾舅,不然就得對(duì)正則中的反斜杠來(lái)進(jìn)行轉(zhuǎn)義
val regEx = """正則表達(dá)式""".r
findAllMatchIn方法
使用findAllMatchIn方法可以獲取到所有正則匹配到的字符串
示例說(shuō)明
- 定義一個(gè)正則表達(dá)式,來(lái)匹配郵箱是否合法
- 合法郵箱測(cè)試:qq12344@163.com
- 不合法郵箱測(cè)試:qq12344@.com
val r = """.+@.+\..+""".r
val eml1 = "qq12344@163.com"
val eml2 = "qq12344@.com"
if(r.findAllMatchIn(eml1).size > 0) {
println(eml1 + "郵箱合法")
}
else {
println(eml1 + "郵箱不合法")
}
if(r.findAllMatchIn(eml2).size > 0) {
println(eml2 + "郵箱合法")
}
else {
println(eml2 + "郵箱不合法")
}
示例說(shuō)明
找出以下列表中的所有不合法的郵箱
"38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com"
val emlList =
List("38123845@qq.com", "a1da88123f@gmail.com", "zhansan@163.com", "123afadff.com")
val regex = """.+@.+\..+""".r
val invalidEmlList = emlList.filter {
x =>
if (regex.findAllMatchIn(x).size < 1) true else false
}
println(invalidEmlList)
異常處理
下面一段代碼
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拋出了異常,而且沒(méi)有打印出來(lái)"你好"署恍。說(shuō)明程序出現(xiàn)錯(cuò)誤后就終止了。
那怎么解決該問(wèn)題呢蜻直?
在scala中盯质,可以使用異常處理來(lái)解決這個(gè)問(wèn)題
語(yǔ)法
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í)行的代碼
示例說(shuō)明
- 使用try..catch來(lái)捕獲除零異常
try {
val i = 10 / 0
println("你好呼巷!")
} catch {
case ex: Exception => println(ex.getMessage)
}
拋出異常
我們也可以在一個(gè)方法中,拋出異常赎瑰。語(yǔ)法格式和Java類似王悍,使用throw new Exception...
示例說(shuō)明
- 在main方法中拋出一個(gè)異常
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)
提取器
之前我們學(xué)習(xí)過(guò)了,實(shí)現(xiàn)一個(gè)類的伴生對(duì)象中的apply方法餐曼,可以用類名來(lái)快速構(gòu)建一個(gè)對(duì)象压储。伴生對(duì)象中鲜漩,還有一個(gè)unapply方法。與apply相反集惋,unapply是將該類的對(duì)象孕似,拆解為一個(gè)個(gè)的元素。
語(yǔ)法
def unapply(stu:Student):Option[(類型1, 類型2, 類型3...)] = {
if(stu != null) {
Some((變量1, 變量2, 變量3...))
}
else {
None
}
}
示例說(shuō)明
- 創(chuàng)建一個(gè)Student類刮刑,包含姓名年齡兩個(gè)字段
- 實(shí)現(xiàn)一個(gè)類的解構(gòu)器喉祭,并使用match表達(dá)式進(jìn)行模式匹配,提取類中的字段雷绢。
class Student(var name:String, var age:Int)
object Student {
def apply(name:String, age:Int) = {
new Student(name, age)
}
def unapply(student:Student) = {
val tuple = (student.name, student.age)
Some(tuple)
}
}
def main(args: Array[String]): Unit = {
val zhangsan = Student("張三", 20)
zhangsan match {
case Student(name, age) => println(s"${name} => ${age}")
}
}
泛型
scala和Java一樣泛烙,類和特質(zhì)、方法都可以支持泛型翘紊。我們?cè)趯W(xué)習(xí)集合的時(shí)候蔽氨,一般都會(huì)涉及到泛型。
scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)
語(yǔ)法
def 方法名[泛型名稱](..) = {
//...
}
不考慮泛型
ef getMiddle(arr:Array[Int]) = arr(arr.length / 2)
def main(args: Array[String]): Unit = {
val arr1 = Array(1,2,3,4,5)
println(getMiddle(arr1))
}
加入泛型之后
def getMiddleElement[T](array:Array[T]) =
array(array.length / 2)
def main(args: Array[String]): Unit = {
println(getMiddleElement(Array(1, 2, 3, 4, 5)))
println(getMiddleElement(Array("a", "b", "c", "d", "e")))
}
泛型類
scala的類也可以定義泛型霞溪。接下來(lái)孵滞,我們來(lái)學(xué)習(xí)如何定義scala的泛型類
語(yǔ)法
class 類[T](val 變量名: T)
示例說(shuō)明
- 實(shí)現(xiàn)一個(gè)Pair泛型類
- Pair類包含兩個(gè)字段,而且兩個(gè)字段的類型不固定
- 創(chuàng)建不同類型泛型類對(duì)象鸯匹,并打印
case class Pair[T](var a:T, var b:T)
def main(args: Array[String]): Unit = {
val pairList = List(
Pair("Hadoop", "Storm"),
Pair("Hadoop", 2008),
Pair(1.0, 2.0),
Pair("Hadoop", Some(1.9))
)
println(pairList)
}
泛型的上下界
我們?cè)诙x方法/類的泛型時(shí)坊饶,限定必須從哪個(gè)類繼承、或者必須是哪個(gè)類的父類殴蓬。此時(shí)匿级,就需要使用到上下界。
上界定義
使用<: 類型名
表示給類型添加一個(gè)上界染厅,表示泛型參數(shù)必須要從該類(或本身)繼承
[T <: 類型]
示例說(shuō)明
- 定義一個(gè)Person類
- 定義一個(gè)Student類痘绎,繼承Person類
- 定義一個(gè)demo泛型方法,該方法接收一個(gè)Array參數(shù)肖粮,
- 限定demo方法的Array元素類型只能是Person或者Person的子類
- 測(cè)試調(diào)用demo孤页,傳入不同元素類型的Array
class Person
class Student extends Person
def demo[T <: Person](a:Array[T]) = println(a)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Student))
// 編譯出錯(cuò),必須是Person的子類
// demo(Array("hadoop"))
}
下界定義
上界是要求必須是某個(gè)類的子類涩馆,或者必須從某個(gè)類繼承行施,而下界是必須是某個(gè)類的父類(或本身)
[T >: 類型]
示例說(shuō)明
- 定義一個(gè)Person類
- 定義一個(gè)Policeman類,繼承Person類
- 定義一個(gè)Superman類魂那,繼承Policeman類
- 定義一個(gè)demo泛型方法蛾号,該方法接收一個(gè)Array參數(shù),
- 限定demo方法的Array元素類型只能是Person涯雅、Policeman
- 測(cè)試調(diào)用demo鲜结,傳入不同元素類型的Array
class Person
class Policeman extends Person
class Superman extends Policeman
def demo[T >: Policeman](array:Array[T]) = println(array)
def main(args: Array[String]): Unit = {
demo(Array(new Person))
demo(Array(new Policeman))
// 編譯出錯(cuò):Superman是Policeman的子類
// demo(Array(new Superman))
}
協(xié)變、逆變、非變 (掌握)
spark的源代碼中大量使用到了協(xié)變精刷、逆變拗胜、非變,學(xué)習(xí)該知識(shí)點(diǎn)對(duì)我們將來(lái)閱讀spark源代碼很有幫助贬养。
class Pair[T]
object Pair {
def main(args: Array[String]): Unit = {
val p1 = Pair("hello")
// 編譯報(bào)錯(cuò)挤土,無(wú)法將p1轉(zhuǎn)換為p2
val p2:Pair[AnyRef] = p1
println(p2)
}
}
非變
class Pair[T]{}
- 默認(rèn)泛型類是非變的
- 類型B是A的子類型,Pair[A]和Pair[B]沒(méi)有任何從屬關(guān)系
- Java是一樣的
協(xié)變
class Pair[+T]
- 類型B是A的子類型误算,Pair[B]可以認(rèn)為是Pair[A]的子類型
- 參數(shù)化類型的方向和類型的方向是一致的仰美。
逆變
class Pair[-T]
- 類型B是A的子類型,Pair[A]反過(guò)來(lái)可以認(rèn)為是Pair[B]的子類型
- 參數(shù)化類型的方向和類型的方向是相反的
示例
- 定義一個(gè)Super類儿礼、以及一個(gè)Sub類繼承自Super類
- 使用協(xié)變咖杂、逆變、非變分別定義三個(gè)泛型類
- 分別創(chuàng)建泛型類來(lái)演示協(xié)變蚊夫、逆變诉字、非變
class Super
class Sub extends Super
class Temp1[T]
class Temp2[+T]
class Temp3[-T]
def main(args: Array[String]): Unit = {
val a:Temp1[Sub] = new Temp1[Sub]
// 編譯報(bào)錯(cuò)
// 非變
//val b:Temp1[Super] = a
// 協(xié)變
val c: Temp2[Sub] = new Temp2[Sub]
val d: Temp2[Super] = c
// 逆變
val e: Temp3[Super] = new Temp3[Super]
val f: Temp3[Sub] = e
}
Actor 介紹
Actor并發(fā)編程模型,是scala提供給程序員的一種與Java并發(fā)編程完全不一樣的并發(fā)編程模型知纷,是一種基于事件模型的并發(fā)機(jī)制壤圃。Actor并發(fā)編程模型是一種不共享數(shù)據(jù),依賴消息傳遞的一種并發(fā)編程模式琅轧,有效避免資源爭(zhēng)奪伍绳、死鎖等情況。
創(chuàng)建Actor
創(chuàng)建Actor的方式和Java中創(chuàng)建線程很類似乍桂,也是通過(guò)繼承來(lái)創(chuàng)建冲杀。
- 定義class或object繼承Actor特質(zhì)
- 重寫act方法
- 調(diào)用Actor的start方法執(zhí)行Actor
示例說(shuō)明
創(chuàng)建兩個(gè)Actor,一個(gè)Actor打印1-10睹酌,另一個(gè)Actor打印11-20
- 使用class繼承Actor創(chuàng)建(如果需要在程序中創(chuàng)建多個(gè)相同的Actor)
- 使用object繼承Actor創(chuàng)建(如果在程序中只創(chuàng)建一個(gè)Actor)
object _05ActorDemo {
class Actor1 extends Actor {
override def act(): Unit = (1 to 10).foreach(println(_))
}
class Actor2 extends Actor {
override def act(): Unit = (11 to 20).foreach(println(_))
}
def main(args: Array[String]): Unit = {
new Actor1().start()
new Actor2().start()
}
}
使用object繼承Actor創(chuàng)建
object Actor1 extends Actor {
override def act(): Unit =
for(i <- 1 to 10) {
println(i)
}
}
object Actor2 extends Actor {
override def act(): Unit =
for(i <- 11 to 20) {
println(i)
}
}
def main(args: Array[String]): Unit = {
Actor1.start()
Actor2.start()
}
Actor程序運(yùn)行流程
- 調(diào)用start()方法啟動(dòng)Actor
- 自動(dòng)執(zhí)行act()方法
- 向Actor發(fā)送消息
- act方法執(zhí)行完成后权谁,程序會(huì)調(diào)用exit()方法
Actor 發(fā)送接收消息
我們之前介紹Actor的時(shí)候,說(shuō)過(guò)Actor是基于事件(消息)的并發(fā)編程模型憋沿,那么Actor是如何發(fā)送消息和接收消息的呢旺芽?
! | 發(fā)送異步消息辐啄,沒(méi)有返回值 |
---|---|
!? | 發(fā)送同步消息甥绿,等待返回值 |
!! | 發(fā)送異步消息,返回值是Future[Any] |
要給actor1發(fā)送一個(gè)異步字符串消息则披,使用以下代碼:
ctor1 ! "你好!"
接收消息
Actor中使用receive方法來(lái)接收消息,需要給receive方法傳入一個(gè)偏函數(shù)
{
case 變量名1:消息類型1 => 業(yè)務(wù)處理1,
case 變量名2:消息類型2 => 業(yè)務(wù)處理2,
...
}
示例說(shuō)明
- 創(chuàng)建兩個(gè)Actor(ActorSender洗出、ActorReceiver)
- ActorSender發(fā)送一個(gè)異步字符串消息給ActorReceiver
- ActorReceive接收到該消息后士复,打印出來(lái)
object ActorSender extends Actor {
override def act(): Unit = {
// 發(fā)送消息
while(true) {
ActorReceiver ! "hello!"
TimeUnit.SECONDS.sleep(3)
}
}
}
object ActorReceiver extends Actor {
override def act(): Unit = {
// 持續(xù)接收消息
while(true) {
receive {
case msg:String => println("接收到消息:" + msg)
}
}
}
}
def main(args: Array[String]): Unit = {
ActorReceiver.start()
ActorSender.start()
}
Actor 的Loop
上述代碼,使用while循環(huán)來(lái)不斷接收消息。
- 如果當(dāng)前Actor沒(méi)有接收到消息阱洪,線程就會(huì)處于阻塞狀態(tài)
- 如果有很多的Actor便贵,就有可能會(huì)導(dǎo)致很多線程都是處于阻塞狀態(tài)
- 每次有新的消息來(lái)時(shí),重新創(chuàng)建線程來(lái)處理
- 頻繁的線程創(chuàng)建冗荸、銷毀和切換承璃,會(huì)影響運(yùn)行效率
在scala中,可以使用loop + react來(lái)復(fù)用線程蚌本。比while + receive更高效
用Loop改寫上述代碼
// 持續(xù)接收消息
loop {
react {
case msg:String => println("接收到消息:" + msg)
}
}
Actor 舉例說(shuō)明
示例一
示例說(shuō)明
- 創(chuàng)建一個(gè)MsgActor盔粹,并向它發(fā)送一個(gè)同步消息,該消息包含兩個(gè)字段(id程癌、message)
- MsgActor回復(fù)一個(gè)消息舷嗡,該消息包含兩個(gè)字段(message、name)
- 打印回復(fù)消息
case class Message(id:Int, msg:String)
case class ReplyMessage(msg:String, name:String)
object MsgActor extends Actor {
override def act(): Unit = {
loop {
react {
case Message(id, msg) => {
println(s"接收到消息:${id}/${msg}")
sender ! ReplyMessage("不太好", "Tom")
}
}
}
}
}
def main(args: Array[String]): Unit = {
MsgActor.start()
val replyMessage: Any = MsgActor !? Message(1, "你好")
println("回復(fù)消息:" + replyMessage.asInstanceOf[ReplyMessage])
}