Scala語言學(xué)習(xí)五 (高級用法)

Scala 高級用法

樣例類

? 樣例類是一種特殊類,它可以用來快速定義一個用于保存數(shù)據(jù)的類(類似于Java POJO類),在后續(xù)要學(xué)習(xí)并發(fā)編程和spark、flink這些框架也都會經(jīng)常使用它。

語法

ase class 樣例類名([var/val] 成員變量名1:類型1, 成員變量名2:類型2, 成員變量名3:類型3)    

示例:

需求

  • 定義一個Person樣例類,包含姓名和年齡成員變量
  • 創(chuàng)建樣例類的對象實(shí)例("張三"、20),并打印它
object _01CaseClassDemo {
  case class Person(name:String, age:Int)

  def main(args: Array[String]): Unit = {
    val zhangsan = Person("張三", 20)

    println(zhangsan)
  }
}

需求

  • 定義一個Person樣例類,包含姓名和年齡成員變量
  • 創(chuàng)建樣例類的對象實(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)我們定義一個樣例類,編譯器自動幫助我們實(shí)現(xiàn)了以下幾個有用的方法:

  • apply方法
  • toString方法
  • equals方法
  • hashCode方法
  • copy方法

apply方法

apply方法可以讓我們快速地使用類名來創(chuàng)建對象。參考以下代碼:

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方法

樣例類自動實(shí)現(xiàn)了equals方法,可以直接使用==比較兩個樣例類是否相等,即所有的成員變量是否相等

示例

  • 創(chuàng)建一個樣例類Person,包含姓名、年齡
  • 創(chuàng)建名字年齡分別為"李四", 21的兩個對象
  • 比較它們是否相等
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 21)
println(lisi1 == lisi2)
// 輸出:true

hashCode方法

樣例類自動實(shí)現(xiàn)了hashCode方法,如果所有成員變量的值相同,則hash值相同,只要有一個不一樣,則hash值不一樣。

示例

  • 創(chuàng)建名字年齡分別為"李四", 21的對象
  • 再創(chuàng)建一個名字年齡分別為"李四", 22的對象
  • 分別打印這兩個對象的哈希值
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 22)

println(lisi1.hashCode())
println(lisi2.hashCode())

copy方法

樣例類實(shí)現(xiàn)了copy方法,可以快速創(chuàng)建一個相同的實(shí)例對象,可以使用帶名參數(shù)指定給成員進(jìn)行重新賦值

示例

  • 創(chuàng)建名字年齡分別為"李四", 21的對象
  • 通過copy拷貝,名字為"王五"的對象
**示例**

- 創(chuàng)建名字年齡分別為"李四", 21的對象
- 通過copy拷貝,名字為"王五"的對象

樣例對象

語法

case object 樣例對象名

示例

需求說明

  • 定義一個性別Sex枚舉,它只有兩個實(shí)例(男性——Male、女性——Female)
  • 創(chuàng)建一個Person類,它有兩個成員(姓名、性別)
  • 創(chuàng)建兩個Person對象("張三"、男性)、("李四"、"女")
trait Sex /*定義一個性別特質(zhì)*/
case object Male extends Sex        // 定義一個樣例對象并實(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)
  }
}

模式匹配

簡單匹配模式

在Java中,有switch關(guān)鍵字,可以簡化if條件判斷語句。在scala中,可以使用match表達(dá)式替代。

語法

變量 match {
    case "常量1" => 表達(dá)式1
    case "常量2" => 表達(dá)式2
    case "常量3" => 表達(dá)式3
    case _ => 表達(dá)式4      // 默認(rèn)匹配
}

示例

需求說明

  1. 從控制臺輸入一個單詞(使用StdIn.readLine方法)
  2. 判斷該單詞是否能夠匹配以下單詞,如果能匹配,返回一句話
  3. 打印這句話
println("請輸出一個詞:")
// StdIn.readLine表示從控制臺讀取一行文本
val name = StdIn.readLine()

val result = name match {
    case "hadoop" => "大數(shù)據(jù)分布式存儲和計(jì)算框架"
    case "zookeeper" => "大數(shù)據(jù)分布式協(xié)調(diào)服務(wù)框架"
    case "spark" => "大數(shù)據(jù)分布式內(nèi)存計(jì)算框架"
    case _ => "未匹配"
}

println(result)

匹配類型

語法

變量 match {
    case 類型1變量名: 類型1 => 表達(dá)式1
    case 類型2變量名: 類型2 => 表達(dá)式2
    case 類型3變量名: 類型3 => 表達(dá)式3
    ...
    case _ => 表達(dá)式4
}

示例

需求說明

  • 定義一個變量為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,也就是說在沒有匹配到值得情況下得取一個默認(rèn)的值

示例

需求說明

  • 從控制臺讀入一個數(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可以使用模式匹配來匹配樣例類,從而可以快速獲取樣例類中的成員數(shù)據(jù)。后續(xù),我們在開發(fā)Akka案例時,還會用到。

需求說明

  • 創(chuàng)建兩個樣例類Customer、Order
    • Customer包含姓名、年齡字段
    • Order包含id字段
  • 分別定義兩個案例類的對象,并指定為Any類型
  • 使用模式匹配這兩個對象,并分別打印它們的成員變量值
/ 1. 創(chuàng)建兩個樣例類
case class Person(name:String, age:Int)
case class Order(id:String)

def main(args: Array[String]): Unit = {
    // 2. 創(chuàng)建樣例類對象,并賦值為Any類型
    val zhangsan:Any = Person("張三", 20)
    val order1:Any = Order("001")

    // 3. 使用match...case表達(dá)式來進(jìn)行模式匹配
    // 獲取樣例類中成員變量
    order1 match {
        case Person(name, age) => println(s"姓名:${name} 年齡:${age}")
        case Order(id1) => println(s"ID為:${id1}")
        case _ => println("未匹配")
    }
}

匹配集合,列表,元祖

匹配集合

scala中的模式匹配,還能用來匹配集合。

示例

  • 依次修改代碼定義以下三個數(shù)組

    Array(1,x,y)   // 以1開頭,后續(xù)的兩個元素不固定
    Array(0)     // 只匹配一個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")
}

匹配列表

示例

  • 依次修改代碼定義以下三個列表

    List(0)               // 只保存0一個元素的列表
    List(0,...)           // 以0開頭的列表,數(shù)量不固定
    List(x,y)         // 只包含兩個元素的列表  
    
  • 使用模式匹配上述列表

val list = List(0, 1, 2)

list match {
    case 0 :: Nil => println("只有0的列表")
    case 0 :: tail => println("0開頭的列表")
    case x :: y :: Nil => println(s"只有另兩個元素${x}, ${y}的列表")
    case _ => println("未匹配")
}

匹配元祖

  • 依次修改代碼定義以下兩個元組

    (1, x, y)     // 以1開頭的、一共三個元素的元組
    (x, y, 5)   // 一共有三個元素,最后一個元素為5的元組
    
  • 使用模式匹配上述元素

val tuple = (2, 2, 5)

tuple match {
    case (1, x, y) => println(s"三個元素,1開頭的元組:1, ${x}, ${y}")
    case (x, y, 5) => println(s"三個元素,5結(jié)尾的元組:${x}, ${y}, 5")
    case _ => println("未匹配")
}

Option 類型

使用Option類型,可以用來有效避免空引用(null)異常。也就是說,將來我們返回某些數(shù)據(jù)時,可以返回一個Option類型來替代。

  • Some(x):表示實(shí)際的值

  • None:表示沒有值

  • 使用getOrElse方法,當(dāng)值為None是可以指定一個默認(rèn)值

示例說明

  • 定義一個兩個數(shù)相除的方法,使用Option類型來封裝結(jié)果
  • 然后使用模式匹配來打印結(jié)果
    • 不是除零,打印結(jié)果
    • 除零打印異常錯誤
  /**
    * 定義除法操作
    * @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("除零異常")
    }
  } 

示例說明

  • 重寫上述案例,使用getOrElse方法,當(dāng)除零時,或者默認(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ù)可以提供了簡潔的語法,可以簡化函數(shù)的定義。配合集合的函數(shù)式編程,可以讓代碼更加優(yōu)雅。

定義

  • 偏函數(shù)被包在花括號內(nèi)沒有match的一組case語句是一個偏函數(shù)

  • 偏函數(shù)是PartialFunction[A, B]的一個實(shí)例

    • A代表輸入?yún)?shù)類型
    • B代表返回結(jié)果類型

示例說明*

定義一個偏函數(shù),根據(jù)以下方式返回

1->一

2->二

3->三

其他 —> 其他

// func1是一個輸入?yún)?shù)為Int類型,返回值為String類型的偏函數(shù)
val func1: PartialFunction[Int, String] = {
    case 1 => "一"
    case 2 => "二"
    case 3 => "三"
    case _ => "其他"
}

println(func1(2))

示例說明

  • 定義一個列表,包含1-10的數(shù)字
  • 請將1-3的數(shù)字都轉(zhuǎn)換為[1-3]
  • 請將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類來定義正則表達(dá)式

  • 要構(gòu)造一個RegEx對象,直接使用String類的r方法即可

  • 建議使用三個雙引號來表示正則表達(dá)式,不然就得對正則中的反斜杠來進(jìn)行轉(zhuǎn)義

val regEx = """正則表達(dá)式""".r

findAllMatchIn方法

使用findAllMatchIn方法可以獲取到所有正則匹配到的字符串

示例說明

  • 定義一個正則表達(dá)式,來匹配郵箱是否合法
  • 合法郵箱測試:qq12344@163.com
  • 不合法郵箱測試: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 + "郵箱不合法")
}

示例說明

找出以下列表中的所有不合法的郵箱

"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拋出了異常,而且沒有打印出來"你好"。說明程序出現(xiàn)錯誤后就終止了。

那怎么解決該問題呢?

在scala中,可以使用異常處理來解決這個問題

語法

try {
    // 代碼
}
catch {
    case ex:異常類型1 => // 代碼
    case ex:異常類型2 => // 代碼
}
finally {
    // 代碼
}
  • try中的代碼是我們編寫的業(yè)務(wù)處理代碼
  • 在catch中表示當(dāng)出現(xiàn)某個異常時,需要執(zhí)行的代碼
  • 在finally中,是不管是否出現(xiàn)異常都會執(zhí)行的代碼

示例說明

  • 使用try..catch來捕獲除零異常
try {
    val i = 10 / 0

    println("你好!")
} catch {
    case ex: Exception => println(ex.getMessage)
} 

拋出異常

我們也可以在一個方法中,拋出異常。語法格式和Java類似,使用throw new Exception...

示例說明

  • 在main方法中拋出一個異常
  def main(args: Array[String]): Unit = {
    throw new Exception("這是一個異常")
  }

Exception in thread "main" java.lang.Exception: 這是一個異常
    at ForDemo$.main(ForDemo.scala:3)
    at ForDemo.main(ForDemo.scala)

提取器

之前我們學(xué)習(xí)過了,實(shí)現(xiàn)一個類的伴生對象中的apply方法,可以用類名來快速構(gòu)建一個對象。伴生對象中,還有一個unapply方法。與apply相反,unapply是將該類的對象,拆解為一個個的元素。

1552639637165
1552639674932

語法

def unapply(stu:Student):Option[(類型1, 類型2, 類型3...)] = {
    if(stu != null) {
        Some((變量1, 變量2, 變量3...))
    }
    else {
        None
    }
}

示例說明

  • 創(chuàng)建一個Student類,包含姓名年齡兩個字段
  • 實(shí)現(xiàn)一個類的解構(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ì)、方法都可以支持泛型。我們在學(xué)習(xí)集合的時候,一般都會涉及到泛型。

scala> val list1:List[String] = List("1", "2", "3")
list1: List[String] = List(1, 2, 3)

語法

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的類也可以定義泛型。接下來,我們來學(xué)習(xí)如何定義scala的泛型類

語法

class 類[T](val 變量名: T)

示例說明

  • 實(shí)現(xiàn)一個Pair泛型類
  • Pair類包含兩個字段,而且兩個字段的類型不固定
  • 創(chuàng)建不同類型泛型類對象,并打印
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)
}

泛型的上下界

我們在定義方法/類的泛型時,限定必須從哪個類繼承、或者必須是哪個類的父類。此時,就需要使用到上下界。

上界定義

使用<: 類型名表示給類型添加一個上界,表示泛型參數(shù)必須要從該類(或本身)繼承

[T <: 類型]

示例說明

  • 定義一個Person類
  • 定義一個Student類,繼承Person類
  • 定義一個demo泛型方法,該方法接收一個Array參數(shù),
  • 限定demo方法的Array元素類型只能是Person或者Person的子類
  • 測試調(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))
    // 編譯出錯,必須是Person的子類
    // demo(Array("hadoop"))
}

下界定義

上界是要求必須是某個類的子類,或者必須從某個類繼承,而下界是必須是某個類的父類(或本身)

[T >: 類型]

示例說明

  • 定義一個Person類
  • 定義一個Policeman類,繼承Person類
  • 定義一個Superman類,繼承Policeman類
  • 定義一個demo泛型方法,該方法接收一個Array參數(shù),
  • 限定demo方法的Array元素類型只能是Person、Policeman
  • 測試調(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))
    // 編譯出錯:Superman是Policeman的子類
    // demo(Array(new Superman))
}

協(xié)變、逆變、非變 (掌握)

spark的源代碼中大量使用到了協(xié)變、逆變、非變,學(xué)習(xí)該知識點(diǎn)對我們將來閱讀spark源代碼很有幫助。

class Pair[T]

object Pair {
  def main(args: Array[String]): Unit = {
    val p1 = Pair("hello")
    // 編譯報(bào)錯,無法將p1轉(zhuǎn)換為p2
    val p2:Pair[AnyRef] = p1

    println(p2)
  }
}
1558064807949

非變

class Pair[T]{}
  • 默認(rèn)泛型類是非變的
  • 類型B是A的子類型,Pair[A]和Pair[B]沒有任何從屬關(guān)系
  • Java是一樣的

協(xié)變

class Pair[+T]
  • 類型B是A的子類型,Pair[B]可以認(rèn)為是Pair[A]的子類型
  • 參數(shù)化類型的方向和類型的方向是一致的。

逆變

class Pair[-T]
  • 類型B是A的子類型,Pair[A]反過來可以認(rèn)為是Pair[B]的子類型
  • 參數(shù)化類型的方向和類型的方向是相反的

示例

  • 定義一個Super類、以及一個Sub類繼承自Super類
  • 使用協(xié)變、逆變、非變分別定義三個泛型類
  • 分別創(chuàng)建泛型類來演示協(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)錯
    // 非變
    //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ā)編程模式,有效避免資源爭奪、死鎖等情況。

1552787528554

創(chuàng)建Actor

創(chuàng)建Actor的方式和Java中創(chuàng)建線程很類似,也是通過繼承來創(chuàng)建。

  1. 定義class或object繼承Actor特質(zhì)
  2. 重寫act方法
  3. 調(diào)用Actor的start方法執(zhí)行Actor

示例說明

創(chuàng)建兩個Actor,一個Actor打印1-10,另一個Actor打印11-20

  • 使用class繼承Actor創(chuàng)建(如果需要在程序中創(chuàng)建多個相同的Actor)
  • 使用object繼承Actor創(chuàng)建(如果在程序中只創(chuàng)建一個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)行流程

  1. 調(diào)用start()方法啟動Actor
  2. 自動執(zhí)行act()方法
  3. 向Actor發(fā)送消息
  4. act方法執(zhí)行完成后,程序會調(diào)用exit()方法

Actor 發(fā)送接收消息

我們之前介紹Actor的時候,說過Actor是基于事件(消息)的并發(fā)編程模型,那么Actor是如何發(fā)送消息和接收消息的呢?

發(fā)送異步消息,沒有返回值
!? 發(fā)送同步消息,等待返回值
!! 發(fā)送異步消息,返回值是Future[Any]

要給actor1發(fā)送一個異步字符串消息,使用以下代碼:

ctor1 ! "你好!"

接收消息

Actor中使用receive方法來接收消息,需要給receive方法傳入一個偏函數(shù)

{
    case 變量名1:消息類型1 => 業(yè)務(wù)處理1,
    case 變量名2:消息類型2 => 業(yè)務(wù)處理2,
    ...
}

示例說明

  • 創(chuàng)建兩個Actor(ActorSender、ActorReceiver)
  • ActorSender發(fā)送一個異步字符串消息給ActorReceiver
  • ActorReceive接收到該消息后,打印出來
1552791021244
  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)來不斷接收消息。

  • 如果當(dāng)前Actor沒有接收到消息,線程就會處于阻塞狀態(tài)
  • 如果有很多的Actor,就有可能會導(dǎo)致很多線程都是處于阻塞狀態(tài)
  • 每次有新的消息來時,重新創(chuàng)建線程來處理
  • 頻繁的線程創(chuàng)建、銷毀和切換,會影響運(yùn)行效率

在scala中,可以使用loop + react來復(fù)用線程。比while + receive更高效

用Loop改寫上述代碼

// 持續(xù)接收消息
loop {
    react {
        case msg:String => println("接收到消息:" + msg)
    }
}

Actor 舉例說明

示例一

示例說明

  • 創(chuàng)建一個MsgActor,并向它發(fā)送一個同步消息,該消息包含兩個字段(id、message)
  • MsgActor回復(fù)一個消息,該消息包含兩個字段(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])
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容