1. 程式人生 > >Scala學習之路----高階程式設計

Scala學習之路----高階程式設計

一、函數語言程式設計總述 Scala中的函式是Java中完全沒有的概念。 Java是完全面向物件的程式語言, 沒有任何面向過程程式語言的特性, 因此Java中的一等公民是類 和物件, 而且只有方法的概念, 即寄存和依賴於類和物件中的方法。 Java中的方法是絕對不可能脫 離類和物件獨立存在的。 Scala是一門既面向物件, 又面向過程的語言。 在Scala中有非常好的面向物件的特性, 可以使用 Scala基於面向物件的思想開發大型複雜的系統和工程。在Scala中, 函式與類、 物件等一樣, 都是一等公民。 Scala中的函式可以獨立存在, 不需要依賴任何類和物件。 Scala的函數語言程式設計, 讓Scala具備了Java所不具備的更強大的功能和特性。 1、將函式賦值給變數
Scala中的函式是一等公民, 可以獨立定義, 獨立存在, 而且可以直接將函式作為值賦值給變數。Scala的語法規定, 將函式賦值給變數時, 必須在函式後面加上空格和下劃線。 def sayHello(name: String) { println("Hello, " + name) } val sayHelloFunc = sayHello _ sayHelloFunc("leo") 2、匿名函式 Scala中, 函式也可以不需要命名, 此時函式被稱為匿名函式。 可以直接定義函式之後, 將函式賦值給某個變數; 也可以直接將定義的匿名函式傳入其他函式之中 Scala定義匿名函式的語法規則: (引數名: 引數型別) => 函式體 這種匿名函式的語法必須深刻理解和掌握, 在Spark的中有大量這樣的語法。 例如:val sayHelloFunc = (name: String) => println("Hello, " + name) 3、高階函式
Scala中, 由於函式是一等公民, 因此可以直接將某個函式傳入其他函式, 作為引數。 這個功能是極其強大的, 也是Java這種面向物件的程式語言所不具備的。 接收其他函式作為引數的函式, 也被稱作高階函式( higher-order function) 例1、val sayHelloFunc = (name: String) => println("Hello, " + name) def greeting(func: (String) => Unit, name: String) { func(name) } greeting(sayHelloFunc, "leo") Array(1, 2, 3, 4, 5).map((num: Int) => num * num) 例2、Array(1, 2, 3, 4, 5).map((num: Int) => num * num) 例3、高階函式的另外一個功能是將函式作為返回值 def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name) val greetingFunc = getGreetingFunc("hello") greetingFunc("leo") 4、高階函式的寫法
高階函式可以自動推斷出引數型別, 而不需要寫明型別。 而且對於只有一個引數的函式, 還可以省去其小括號。 def greeting(func: (String) => Unit, name: String) { func(name) } greeting((name: String) => println("Hello, " + name), "leo") greeting((name) => println("Hello, " + name), "leo") greeting(name => println("Hello, " + name), "leo") 5、Scala的常用高階函式 例1、map: 對傳入的每個元素都進行對映, 返回一個處理後的元素 Array(1, 2, 3, 4, 5).map(2 * _) 例2、foreach: 對傳入的每個元素都進行處理,但是沒有返回值 (1 to 9).map("*" * _).foreach(println _) 例3、filter: 對傳入的每個元素都進行條件判斷, 如果對元素返回true, 則保留該元素, 否則過濾掉該元素 (1 to 20).filter(_ % 2 == 0) 例4、reduceLeft: 從左側元素開始, 進行reduce操作, 即先對元素1和元素2 進行處理, 然後將結果與元素3處理, 再將結果與元素4處理, 依次類推, 右面這個操作就相當於1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 , (1 to 9).reduceLeft( _ * _) 例5、sortWith: 對元素進行兩兩相比, 進行排序 Array(3, 2, 5, 4, 10, 1).sortWith(_ < _) 二、函數語言程式設計的集合操作 1、Scala的集合體繫結構 Scala中的集合體系主要包括: Iterable、 Seq、 Set、 Map。 其中, Iterable是所有集合trait的根trait, 這個結構與Java的集合體系非常相似。 Scala中的集合是分成可變和不可變兩類集合的。 其中, 可變集合就是說, 集合的元素可以動態修改,而不可變集合的元素在初始化之後, 就無法修改了。分別對應scala.collection.mutable和scala.collection.immutable兩個包。 Seq下包含了Range、 ArrayBuffer、 List等子trait。 其中Range就代表了一個序列, 通常可以使用“ 1 to 10” 這種語法來產生一個Range。 ArrayBuffer就類似於Java中的ArrayList。 2、List List代表一個不可變的列表, List的建立, val list = List(1, 2, 3, 4) List有head和tail,head代表List的第一個元素,tail代表第一個元素之後的所有元素, list.hea,list.tail List有特殊的::操作符, 可以用於將head和tail合併成一個List, 0 :: list 如果一個List只有一個元素, 那麼它的head就是這個元素, 它的tail是Nil。 Nil是一個空的List, 定義為List[Nothing]。 案例: 用遞迴函式來給List中每個元素都加上指定字首, 並列印加上字首的元素 def decorator(list: List[Int], prefix: String) { if (list != Nil) { println(prefix + list.head) decorator(list.tail, prefix) } } 3、LinkedList LinkedList代表一個可變的列表, 使用elem可以引用其頭部, 使用next可以引用其尾部 val list= scala.collection.mutable.LinkedList(1, 2, 3, 4, 5); list.elem; list.next 案例1: 使用while迴圈將LinkedList中的每個元素都乘以2 val list= scala.collection.mutable.LinkedList(1, 2, 3, 4, 5) var currentList =list while (currentList != Nil) { currentList.elem = currentList.elem * 2 currentList = currentList.next } 案例2: 使用while迴圈將LinkedList中, 從第一個元素開始, 每隔一個元素, 乘以2 val list = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) var currentList = list var first = true while (currentList != Nil && currentList.next != Nil) { if (first) { currentList.elem = currentList.elem * 2; first = false } currentList = currentList.next.next if (currentList != Nil) currentList.elem = currentList.elem * 2 } 4、Set Set代表一個沒有重複元素的集合 將重複元素加入Set是沒有用的, 比如val s = Set(1, 2, 3); s + 1; s + 4 而且Set是不保證插入順序的, 也就是說, Set中的元素是亂序的, val s = new scala.collection.mutable.HashSet[Int](); s += 1; s += 2; s += 5 LinkedHashSet會用一個連結串列維護插入順序, val s = new scala.collection.mutable.LinkedHashSet[Int](); s+= 1; s += 2; s += 5 SrotedSet會自動根據key來進行排序, val s = scala.collection.mutable.SortedSet("orange", "apple", "banana") 5、集合的函數語言程式設計 map案例實戰: 為List中每個元素都新增一個字首 List("Leo", "Jen", "Peter", "Jack").map("name is " + _) flatMap案例實戰: 將List中的多行句子拆分成單詞 List("Hello World", "You Me").flatMap(_.split(" ")) foreach案例實戰: 列印List中的每個單詞 List("I", "have", "a", "beautiful", "house").foreach(println(_)) zip案例實戰: 對學生姓名和學生成績進行關聯 List("Leo", "Jen", "Peter", "Jack").zip(List(100, 90, 75, 83)) 函數語言程式設計綜合案例:統計多個文字內的單詞總數 使用scala的io包將文字檔案內的資料讀取出來 val lines01 = scala.io.Source.fromFile("C://Users//Administrator//Desktop//test01.txt").mkString val lines02 = scala.io.Source.fromFile("C://Users//Administrator//Desktop//test02.txt").mkString 使用List的伴生物件, 將多個檔案內的內容建立為一個List val lines = List(lines01, lines02) Scala程式設計的精髓是函數語言程式設計, 也是Scala相較於Java等程式語言最大的功能優勢所在。 Spark的原始碼中大量使用了這種複雜的鏈式呼叫的函數語言程式設計。 Spark本身提供的開發人員使用的程式設計API的風格, 完全沿用了Scala的函數語言程式設計, 比如Spark自身的API中就提供了map、 flatMap、 reduce、 foreach, 以及更高階的reduceByKey、 groupByKey等高階函式。如果要使用Scala進行Spark工程的開發, 那麼就必須掌握這種複雜的高階函式的鏈式呼叫。 lines.flatMap(_.split(" ")).map((_, 1)).map(_._2).reduceLeft(_ + _) 三、模式匹配 模式匹配是Scala中非常有特色, 非常強大的一種功能。 模式匹配, 其實類似於Java中的swich case語法, 即對一個值進行條件判斷, 然後針對不同的條件, 進行不同的處理。 但是Scala的模式匹配的功能比Java的swich case語法的功能要強大地多, Java的swich case語法只能對值進行匹配。 Scala的模式匹配除了可以對值進行匹配之外, 還可以對型別進行匹配、 對Array和List的元素情況進行匹配、 對case class( 樣例類) 進行匹配、 甚至對有值或沒值( Option) 進行匹配。 對於Spark來說,Scala的模式匹配功能也是極其重要的,在Spark原始碼中大量地使用了模式匹配功能 1、模式匹配的基礎語法 Scala是沒有Java中的switch case語法的, 相對應的, Scala提供了更加強大的match case語法, 即模式匹配。 Scala的match case與Java的switch case最大的不同點在於, Java的switch case僅能匹配變數的值, 比1、 2、3等; 而Scala的match case可以匹配各種情況, 比如變數的型別、 集合的元素、 有值或無值。 match case的語法如下: 變數 match { case 值 => 程式碼 }。 如果值為下劃線, 則代表了不滿足以上所有情況下的預設情況如何處理。 此外, match case中, 只要一個case分支滿足並處理了, 就不會繼續判斷下一個case分支了。 ( 與Java不同, java的switch case需要用break阻止) match case語法最基本的應用, 就是對變數的值進行模式匹配 案例: 成績評價 def judgeGrade(grade: String) { grade match { case "A" => println("Excellent") case "B" => println("Good") case "C" => println("Just so so") case _ => println("you need work harder") } } 1》在模式匹配中使用if守衛 Scala的模式匹配語法, 有一個特點在於, 可以在case後的條件判斷中, 不僅僅只是提供一個值, 而是可以在值後面再加一個if守衛, 進行雙重過濾。 案例: 成績評價(守衛版) def judgeGrade(name: String, grade: String) { grade match { case "A" => println(name + ", you are excellent") case "B" => println(name + ", you are good") case "C" => println(name + ", you are just so so") case _ if name == "leo" => println(name + ", you are a good boy, come on") case _ => println("you need to work harder") } } 2》在模式匹配中進行變數賦值 Scala的模式匹配語法,有一個特點在於,可以將模式匹配的預設情況,下劃線,替換為一個變數名 此時模式匹配語法就會將要匹配的值賦值給這個變數,從而可以在後面的處理語句中使用要匹配的值 案例: 成績評價(賦值版) def judgeGrade(name: String, grade: String) { grade match { case "A" => println(name + ", you are excellent") case "B" => println(name + ", you are good") case "C" => println(name + ", you are just so so") case _grade if name == "leo" => println(name + ", you are a good boy, come on, your grade is " + _grade) case _grade => println("you need to work harder, your grade is " + _grade) } } 2、對型別進行模式匹配 Scala的模式匹配一個強大之處就在於, 可以直接匹配型別, 而不是值。 這點是Java的switch case絕對做不到的。 匹配型別的話, 需要用“ case變數:型別 => 程式碼” 這種語法, 而不是匹配值的“ case 值 => 程式碼” 這種語法。 案例: 異常處理 import java.io._ def processException(e: Exception) { e match { case e1: IllegalArgumentException => println("you have illegal arguments! exception is: " + e1) case e2: FileNotFoundException => println("cannot find the file you need read or write!, exception is: " + e2) case e3: IOException => println("you got an error while you were doing IO operation! exception is: " + e3) case _: Exception => println("cannot know which exception you have!" ) } } 3、對Array和List的元素進行模式匹配 1》對Array進行模式匹配, 分別可以匹配: 帶有指定元素的陣列、 帶有指定個數元素的陣列、 以某元素打頭的陣列 案例: def greeting(arr: Array[String]) { arr match { case Array("Leo") => println("Hi, Leo!") case Array(girl1, girl2, girl3) => println("Hi"+girl1 + " and " + girl2 + " and " + girl3) case Array("Leo", _*) => println("Hello, Leo") case _ => println("hey, who are you?") } } 2》對List進行模式匹配, 與Array類似, 但是需要使用List特有的::操作符 案例: def greeting(list: List[String]) { list match { case "Leo" :: Nil => println("Hi, Leo!") case girl1 :: girl2 :: girl3 :: Nil => println("Hi " + girl1 + " and " + girl2 + " and " + girl3) case "Leo" :: tail => println("Hi, Leo.") case _ => println("hey, who are you?") } } 4、case class(樣例類)的模式匹配 Scala中提供了一種特殊的類, 用case class進行宣告, 中文也可以稱作樣例類。 case class其實有點類似於Java中的JavaBean的概念, 即只定義field, 並且由Scala編譯時 自動提供getter和setter方法, 但是沒有method。 case class的主建構函式接收的引數通常不需要使用var或val修飾, Scala自動就會使用 val修飾。 Scala自動為case class定義了伴生物件, 也就是object, 並且定義了apply()方法, 該方 法接收主建構函式中相同的引數, 並返回case class物件 案例: class Person case class Teacher(name: String, subject: String) extends Person case class Student(name: String, classroom: String) extends Person def judgeIdentify(p: Person) { p match { case Teacher(name, subject) => println("Teacher, name is " + name + ", subject is " + subject) case Student(name, classroom) => println("Student, name is " + name + ", classroom is " + classroom) case _ => println("Illegal access, please go out of the school!") } } 5、Option的模式匹配 Scala有一種特殊的型別, 叫做Option。 Option有兩種值, 一種是Some, 表示有值, 一種是None, 表示沒有值。 Option通常會用於模式匹配中, 用於判斷某個變數是有值還是沒有值, 這比null來的更加簡潔明瞭 案例: 成績查詢 val grades = Map("Leo" -> "A", "Jack" -> "B", "Jen" -> "C") def getGrade(name: String) { val grade = grades.get(name) grade match { case Some(grade) => println("your grade is " + grade) case None => println("Sorry!") } } 四、型別引數 Java中提出了泛型的概念, 也就是型別引數的概念。 此時可以用泛型建立List, List a = new ArrayList[Int](), 那麼, 此時a.add(1)沒問題, 而a.add("2")呢? 就不行了, 因為泛型會限制, 只能往集合中新增Integer型別, 這樣就避免了上述的問題。 Scala的型別引數與Java的泛型是一樣的, 也是定義一種型別引數, 比如在集合、 類、 函式中, 定義型別引數, 然後就可以保證使用到該型別引數的地方, 肯定也只能是這種型別, 從而實現 程式更好的健壯性。 1、泛型類 泛型類, 顧名思義, 其實就是在類的宣告中, 定義一些泛型型別, 然後在類內部, 比如欄位或者方法, 就可以使用這些泛型型別。 使用泛型類, 通常是需要對類中的某些成員, 比如某些欄位和方法中的引數或變數, 進行統一的型別限制, 這樣可以保證程式更好的健壯性和穩定性。如果不使用泛型進行統一的型別限制, 那麼在後期程式執行過程中, 難免會出現問題, 比如傳入了不希望的型別,導致程式出問題。 在使用類的時候, 比如建立類的物件, 將型別引數替換為實際的型別, 即可。 Scala自動推斷泛型型別特性: 直接給使用了泛型型別的欄位賦值時, Scala會自動進行型別推斷。 案例: 新生報到, 每個學生來自不同的地方, id可能是Int, 可能是String class Student3[T](val local:T){ def getSchool(hukou:T)="S-"+hukou+"-"+local } object fanxing { def main(args: Array[String]): Unit = { val s = new Student3[Int](123) println(s.getSchool(555)) } } 2、泛型函式 泛型函式, 與泛型類類似, 可以給某個函式在宣告時指定泛型型別, 然後在函式體內, 多個變數或者返回值之間, 就可以使用泛型型別進行宣告, 從而對某個特殊的變數, 或者多個變數, 進行強制性的型別限制。與泛型類一樣, 你可以通過給使用了泛型型別的變數傳遞值來讓Scala自動推斷泛型的實際型別, 也可以在呼叫函式時, 手動指定泛型型別。 案例: 卡片售賣機, 可以指定卡片的內容, 內容可以是String型別或Int型別 object Card { def getCard[T](content: T) = { if (content.isInstanceOf[Int]) "card: 001, " + content else if (content.isInstanceOf[String]) "card: this is your card, " + content else "card: " + content } def main(args: Array[String]): Unit = { println(getCard[String]("hello world")) } } 3、 協變和逆變 Scala的協變和逆變是非常有特色的! 完全解決了Java中的泛型的一大缺憾! 舉例來說, Java中, 如果有Professional是Master的子類, 那麼Card[Professionnal]是不是Card[Master]的子類? 答案是: 不是。 因此對於開發程式造成了很多的麻煩。 而Scala中, 只要靈活使用協變和逆變, 就可以解決Java泛型的問題。 1》斜變案例: 進入會場,大師以及大師級別以下的名片都可以進入會場 class Master class Professional extends Master class Card[+T](val name: String) { def enterMeet(card: Card[Master]): Unit = { println("welcome to have this meeting!") } } object MyScala { def main(args: Array[String]): Unit = { val master = new Card[Master]("leo") val profession = new Card[Professional]("hehe") master.enterMeet(master) profession.enterMeet(profession) } } 2》逆變案例: 進入會場,只要專家級別的名片就可以進入會場,如果大師級別的過來了,當然可以了 class Master class Professional extends Master class Card[-T](val name: String) { def enterMeet(card: Card[Professional]): Unit = { println("welcome to have this meeting!") } } object MyScala { def main(args: Array[String]): Unit = { val master = new Card[Master]("leo") val profession = new Card[Professional]("hehe") master.enterMeet(master) profession.enterMeet(profession) } } 五、隱式轉換與隱式引數 Scala提供的隱式轉換和隱式引數功能, 是非常有特色的功能, 是Java等程式語言所沒有的功能。 它可以允許你手動指定, 將某種型別的物件轉換成其他型別的物件。 通過這些功能, 可以實現非常強大, 而且特殊的功能。 Scala的隱式轉換, 其實最核心的就是定義隱式轉換函式, 即implicit conversion function。 定義的隱式轉換函式, 只要在編寫的程式內引入, 就會被Scala自動使用。 Scala會根據隱式轉換函式的簽名, 在程式中使用隱式轉換函式接收的引數型別定義的物件時, 自 動將其傳入隱式轉換函式, 轉換為另外一種型別的物件並返回。 這就是“隱式轉換” 。 隱式轉換函式叫什麼名字是無所謂的, 因為通常不會由使用者手動呼叫, 而是由Scala進行呼叫。 但是如果要使用隱式轉換, 則需要對隱式轉換函式進行匯入。 因此通常建議將隱式轉換函式的名稱命名為“ one2one” 的形式。 Spark原始碼中有大量的隱式轉換和隱式引數, 因此必須精通這種語法。 1、隱式轉換 1》隱式轉換——隱式函式 例1、 class Num {} class richNum(num:Num){ def richInfo(){ println("This is richnum project") } } object ImplicitDemo { //定義一個名稱為num2richNum的隱式函式 implicit def num2richNum(num:Num)=new richNum(num) def main(args: Array[String]): Unit = { val num = new Num num.richInfo() } } 例2、 object IntToStringTest{ implicit def int2String(i:Int)=i.toString() def main(args: Array[String]): Unit = { println(2341.length()) } } 程式碼中呼叫了String型別的length方法,Int型別本身沒有length方法,但是在可用範圍 內定義了可以把Int轉換為String的隱式函式int2String,因此函式編譯通過並執行出正 確的結果。 注:此例項中隱式函式的定義必須定義在使用之前,否則編譯報錯。 2》隱式轉換——匯入隱式函式 import scala.test.implici.IntToStringTest._ object implic { def main(args: Array[String]): Unit = { println(32114551.length()) } } import scala.test.implici.IntToStringTest._ 將IntToStringTest內部的成員匯入到相應的作用域內,否則無法呼叫隱式函式。 3》隱式轉換 要實現隱式轉換, 只要程式可見的範圍內定義隱式轉換函式即可。 Scala會自動使用隱式轉換函式。 隱式轉換函式與普通函式唯一的語法區別就是, 要以implicit開頭, 而且最好要定義函式返回型別。 案例: 特殊售票視窗( 只接受特殊人群, 比如學生、 老人等) class SpecialPerson(var name: String) class Student(val name: String) class Older(val name: String) object TicketDemo { implicit def object2SpecialPerson(p: Object): SpecialPerson = { if (p.getClass == classOf[Student]) { val stu = p.asInstanceOf[Student] new SpecialPerson(stu.name) } else if (p.getClass == classOf[Older]) { val older = p.asInstanceOf[Older] new SpecialPerson(older.name) } else Nil } var ticketnum = 0; def buyTicket(p: SpecialPerson) = { ticketnum += 1 println("T-" + ticketnum) } def main(args: Array[String]): Unit = { val stu=new Student("zhangsan") val old=new Older("lizi") buyTicket(stu) buyTicket(old) } } 2、使用隱式轉換加強現有型別 隱式轉換非常強大的一個功能, 就是可以在不知不覺中加強現有型別的功能。 也就是說, 可以為某個類定義一個加強版的類, 並定義互相之間的隱式轉換, 從而讓源類在使用加強版的方法時, 由Scala自動進行隱式轉換為加強類, 然後再呼叫該方法。 案例: 超人變身 class Man(val name: String) class Superman(val name: String) { def emitLaser = println("emit a laster!") } object ImplicitMan{ implicit def man2superman(man: Man): Superman = new Superman(man.name) def main(args: Array[String]): Unit = { val leo = new Man("leo") leo.emitLaser } } 3、隱式引數 所謂的隱式引數, 指的是在函式或者方法中, 定義一個用implicit修飾的引數, 此時Scala會嘗試找 到一個指定型別的, 用implicit修飾的物件, 即隱式值, 並注入引數。 Scala會在兩個範圍內查詢:一種是當前作用域內可見的val或var定義的隱式變數; 一種是隱式引數型別的伴生物件內的隱式值 案例: 考試簽到 class SignPen { def write(content: String) = println(content) } implicit val signPen = new SignPen def signForExam(name: String) (implicit signPen: SignPen) { signPen.write(name + " come to exam in time.") } 六、閉包 閉包最簡潔的解釋: 函式在變數不處於其有效作用域時, 還能夠對變數進行訪問, 即為閉包 def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name) val greetingFuncHello =getGreetingFunc("hello") val greetingFuncHi =getGreetingFunc("hi") 兩次呼叫getGreetingFunc函式, 傳入不同的msg, 並建立不同的函式返回。 但是, msg只是一個區域性變數, 這種變數超出了其作用域, 還可以使用的情況, 即為閉包。 七、Currying(柯里化)函式 Curring函式, 指的是, 將原來接收兩個引數的一個函式, 轉換為兩個函式, 第一個函式接收原先的第一個引數, 然後返回接收原先第二個引數的第二個函式。 在函式呼叫的過程中, 就變為了兩個函式連續呼叫的形式。 在Spark的原始碼中, 對於()()這種形式很常見。 def sum(a: Int, b: Int) = a + b sum(1, 1) def sum3(a: Int)(b: Int) = a + b sum3(1)(1) 八、Actor Scala的Actor類似於Java中的多執行緒程式設計。 但是不同的是, Scala的Actor提供的模型與多執行緒有所 不同。 Scala的Actor儘可能地避免鎖和共享狀態, 從而避免多執行緒併發時出現資源爭用的情況, 進 而提升多執行緒程式設計的效能。 此外, Scala Actor的這種模型還可以避免死鎖等一系列傳統多執行緒程式設計 的問題。 Spark中使用的分散式多執行緒框架, 是Akka。 Akka也實現了類似Scala Actor的模型, 其核心概念 同樣也是Actor。 因此只要掌握了Scala Actor, 那麼在Spark原始碼研究時, 至少即可看明白Akka Actor相關的程式碼。 但是, 換一句話說, 由於Spark內部有大量的Akka Actor的使用, 因此對於 Scala Actor也至少必須掌握, 這樣才能學習Spark原始碼。 1、 Actor的建立、 啟動和訊息收發 Scala提供了Actor trait來讓我們更方便地進行actor多執行緒程式設計, Actor trait就類似於 Java中的Thread和Runnable一樣, 是基礎的多執行緒基類和介面。 我們只要重寫Actor trait的act方法, 即可實現自己的執行緒執行體, 與Java中重寫run方法類似。 此外, 使用start()方法啟動actor; 使用 ! 符號, 向actor傳送訊息; actor內部使用receive和模式匹配接收訊息 案例: Actor Hello World import scala.actors.Actor class HelloActor extends Actor { def act() { while (true) { receive { case name: String => println("hello " + name) } } } } object TestActor { def main(args: Array[String]): Unit = { val hello = new HelloActor hello.start() hello ! "world" } } 2、 收發case class型別的訊息 Scala的Actor模型與Java的多執行緒模型之間, 很大的一個區別就是, Scala Actor天然支援執行緒之間的精準通訊; 即一個actor可以給其他actor直接傳送訊息。 這個功能是非常強大和方便的。 要給一個actor傳送訊息, 需要使用“ actor ! 訊息” 的語法。 在scala中, 通常建 議使用樣例類, 即case class來作為訊息進行傳送。 然後在actor接收訊息之後, 可以使用scala強大的模式匹配功能來進行不同訊息的處理。 案例: 使用者註冊登入後臺介面 import scala.actors.Actor case class Login(username:String,pwd:String) case class Register(username:String,pwd:String) class ActorRece extends Actor{ def act{ while(true){ receive{ case Login(username,pwd)=>println(username+" "+pwd) case Register(username,pwd)=>println(username+" "+pwd) } } } } object ActorRecevie { def main(args: Array[String]): Unit = { val login = new Login("zhangsan","123") val reg = new Register("hehe","6554") val actt = new ActorRece actt.start() actt ! login;actt ! reg } } 3、 Actor之間互相收發訊息 如果兩個Actor之間要互相收發訊息, 那麼Scala的建議是, 一個actor向另外一個actor發 送訊息時, 同時帶上自己的引用; 其他actor收到自己的訊息時, 直接通過傳送訊息的 actor的引用, 即可以給它回覆訊息。 案例: 打電話 import scala.actors.Actor import javax.xml.ws.Response case class Message(content:String,sender:Actor) class receives extends Actor{ def act(){ while (true) { receive{ case Message(content,sender)=>{ println("leo telephone:"+content);sender ! "I'm leo,please call me 10 minutes after" } } } } } class senders (val recevieActor:Actor) extends Actor{ def act(){ recevieActor!Message("hello,leo,I'm jack",this) receive{ case response:String=>println("jack telephone:"+response) } } } object actorToactor { def main(args: Array[String]): Unit = { val r = new receives val s = new senders(r) s.start() r.start() } }