Scala編程基礎

ScalaJava的關係... 4java

安裝Scala. 4算法

Scala解釋器的使用... 4spring

聲明變量... 5編程

數據類型與操做符... 5windows

函數調用與apply()函數... 5設計模式

if表達式... 6api

語句終結符、塊表達式... 6數組

輸入和輸出... 6安全

循環... 7網絡

高級for循環... 7

函數的定義與調用... 8

在代碼塊中定義包含多行語句的函數體... 8

遞歸函數與返回類型... 8

默認參數... 8

函數調用時帶名參數... 9

變長參數... 9

序列做爲變長參數... 9

過程... 9

lazy... 9

異常... 10

Array. 10

ArrayBuffer. 11

遍歷ArrayArrayBuffer. 11

數組常見操做... 11

使用yield和函數式編程轉換數組... 12

算法案例:移除第一個負數以後的全部負數... 12

算法案例:移除第一個負數以後的全部負數(改良版)... 13

建立Map. 13

訪問Map的元素... 13

修改Map的元素... 13

遍歷Map. 14

SortedMapLinkedHashMap. 14

Map的元素類型—Tuple. 14

定義一個簡單的類... 14

gettersetter. 15

自定義gettersetter. 15

僅暴露fieldgetter方法... 15

private[this]的使用... 16

Java風格的gettersetter方法... 16

輔助constructor. 16

constructor. 17

內部類... 18

object. 18

伴生對象... 19

object繼承抽象類... 19

apply方法... 19

main方法... 20

object來實現枚舉功能... 20

extends. 21

overridesuper. 21

override field. 21

valvar override/實現 def. 23

isInstanceOfasInstanceOf. 24

getClassclassOf. 25

使用模式匹配進行類型判斷... 25

protected. 25

調用父類的constructor. 26

匿名內部類... 26

抽象類... 27

抽象field. 27

trait做爲接口使用... 28

Trait中定義具體方法... 28

Trait中定義具體字段... 29

Trait中定義抽象字段... 29

爲實例混入trait. 29

trait調用鏈... 30

trait中覆蓋抽象方法... 30

混合使用trait的具體方法和抽象方法... 31

trait的構造機制... 31

trait field的初始化... 32

trait繼承class. 33

將函數賦值給變量... 34

匿名函數... 34

高階函數... 34

高階函數的類型推斷... 35

Scala的經常使用高階函數... 35

閉包... 36

SAM轉換... 36

Currying函數... 36

return到外層函數... 37

Scala的集合體繫結構... 37

List. 37

LinkedList. 38

Set. 38

集合的函數式編程... 38

函數式編程綜合案例:統計多個文本內的單詞總數... 39

模式匹配... 39

在模式匹配中使用if守衛... 40

在模式匹配中進行變量賦值... 40

對類型進行模式匹配... 40

ArrayList進行模式匹配... 41

case class與模式匹配... 41

Option與模式匹配... 42

類型參數... 42

泛型類... 42

泛型函數... 43

上邊界Bounds. 43

下邊界Bounds. 44

View Bounds. 45

Context Bounds. 46

Manifest Context Bounds. 46

協變和逆變... 47

Existential Type. 47

隱式轉換... 48

使用隱式轉換增強現有類型... 49

隱式轉換函數做用域與導入... 49

隱式轉換的發生時機... 49

隱式參數... 50

Actor. 50

Actor的建立、啓動和消息收發... 50

收發case class類型的消息... 51

Actor之間互相收發消息... 51

同步消息和Future. 52

   

ScalaJava的關係

ScalaJava的關係是很是緊密的!!

由於Scala是基於Java虛擬機,也就是JVM的一門編程語言。全部Scala的代碼,都須要通過編譯爲字節碼,而後交由Java虛擬機來運行。

因此ScalaJava是能夠無縫互操做的。Scala能夠任意調用Java的代碼。因此ScalaJava的關係是很是很是緊密的。

安裝Scala

·Scala官方網站下載,http://www.scala-lang.org/download/windows版本的安裝包是scala-2.11.7.msi

·使用下載下來的安裝包安裝Scala

·PATH環境變量中,配置$SCALA_HOME/bin目錄。

·windows命令行內便可直接鍵入scala,打開scala命令行,進行scala編程。

Scala解釋器的使用

·REPLRead(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循環)。scala解釋器也被稱爲REPL,會快速編譯scala代碼爲字節碼,而後交給JVM來執行。

·計算表達式:在scala>命令行內,鍵入scala代碼,解釋器會直接返回結果給你。若是你沒有指定變量來存放這個值,那麼值默認的名稱爲res,並且會顯示結果的數據類型,好比IntDoubleString等等。

·例如,輸入1 + 1,會看到res0: Int = 2

·內置變量:在後面能夠繼續使用res這個變量,以及它存放的值。

·例如,2.0 * res0,返回res1: Double = 4.0

·例如,"Hi, " + res0,返回res2: String = Hi, 2

·自動補全:scala>命令行內,可使用Tab鍵進行自動補全。

·例如,輸入res2.to,敲擊Tab鍵,解釋器會顯示出如下選項,toCharArraytoLowerCasetoStringtoUpperCase。由於此時沒法斷定你須要補全的是哪個,所以會提供給你全部的選項。

·例如,輸入res2.toU,敲擊Tab鍵,直接會給你補全爲res2.toUpperCase

聲明變量

·聲明val變量:能夠聲明val變量來存放表達式的計算結果。

·例如,val result = 1 + 1

·後續這些常量是能夠繼續使用的,例如,2 * result

·可是常量聲明後,是沒法改變它的值的,例如,result = 1,會返回error: reassignment to val的錯誤信息。

·聲明var變量:若是要聲明值能夠改變的引用,可使用var變量。

·例如,val myresult = 1myresult = 2

·可是在scala程序中,一般建議使用val,也就是常量,所以好比相似於spark的大型複雜系統中,須要大量的網絡傳輸數據,若是使用var,可能會擔憂值被錯誤的更改。

·Java的大型複雜系統的設計和開發中,也使用了相似的特性,咱們一般會將傳遞給其餘模塊 / 組件 / 服務的對象,設計成不可變類(Immutable Class)。在裏面也會使用java的常量定義,好比final,阻止變量的值被改變。從而提升系統的健壯性(robust,魯棒性),和安全性。

·指定類型:不管聲明val變量,仍是聲明var變量,均可以手動指定其類型,若是不指定的話,scala會自動根據值,進行類型的推斷。

·例如,val name: String = null

·例如,val name: Any = "leo"

·聲明多個變量:能夠將多個變量放在一塊兒進行聲明。

·例如,val name1, name2:String = null

·例如,val num1, num2 = 100

數據類型與操做符

·基本數據類型:ByteCharShortIntLongFloatDoubleBoolean

·乍一看與Java的基本數據類型的包裝類型相同,可是scala沒有基本數據類型與包裝類型的概念,統一都是類。scala本身會負責基本數據類型和引用類型的轉換操做。

·使用以上類型,直接就能夠調用大量的函數,例如,1.toString()1.to(10)

·類型的增強版類型scala使用不少增強類給數據類型增長了上百種加強的功能或函數。

·例如,String類經過StringOps類加強了大量的函數,"Hello".intersect(" World")

·例如,Scala還提供了RichIntRichDoubleRichChar等類型,RichInt就提供了to函數,1.to(10),此處Int先隱式轉換爲RichInt,而後再調用其to函數

·基本操做符:scala的算術操做符與java的算術操做符也沒有什麼區別,好比+-*/%等,以及&|^>><<等。

·可是,在scala中,這些操做符實際上是數據類型的函數,好比1 + 1,能夠寫作1.+(1)

·例如,1.to(10),又能夠寫作1 to 10

·scala中沒有提供++--操做符,咱們只能使用+-,好比counter = 1counter++是錯誤的,必須寫作counter += 1.

函數調用與apply()函數

·函數調用方式:scala中,函數調用也很簡單。

·例如,import scala.math._sqrt(2)pow(2, 4)min(3, Pi)

·不一樣的一點是,若是調用函數時,不須要傳遞參數,則scala容許調用函數時省略括號的,例如,"Hello World".distinct

·apply函數

    ·Scala中的apply函數是很是特殊的一種函數,在Scalaobject中,能夠聲明apply函數。而使用類名()」(嚴格來說應該是對象名()」)的形式,其實就是類名.apply()」(嚴格來說應該是對象名.apply()」)的一種縮寫。一般使用這種方式來構造類的對象,而不是使用「new 類名()」的方式。

·例如,"Hello World"(6),由於在StringOps類中有def apply(n: Int): Char的函數定義,因此"Hello World"(6),其實是"Hello World".apply(6)的縮寫。

·例如,Array(1, 2, 3, 4),其實是用Array objectapply()函數來建立Array類的實例,也就是一個數組。

 

if表達式

·if表達式的定義:Scala中,if表達式是有值的,就是if或者else中最後一行語句返回的值。

    ·例如,val age = 30; if (age > 18) 1 else 0

    ·能夠將if表達式賦予一個變量,例如,val isAdult = if (age > 18) 1 else 0

    ·另一種寫法,var isAdult = -1; if(age > 18) isAdult = 1 else isAdult = 0,可是一般使用上一種寫法

·if表達式的類型推斷:因爲if表達式是有值的,而ifelse子句的值類型可能不一樣,此時if表達式的值是什麼類型呢?Scala會自動進行推斷,取兩個類型的公共父類型。

    ·例如,if(age > 18) 1 else 0,表達式的類型是Int,由於10都是Int

    ·例如,if(age > 18) "adult" else 0,此時ifelse的值分別是StringInt,則表達式的值是AnyAnyStringInt的公共父類型

    ·若是if後面沒有跟else,則默認else的值是Unit,也用()表示,相似於java中的void或者null。例如,val age = 12; if(age > 18) "adult"。此時就至關於if(age > 18) "adult" else ()

·if語句放在多行中:默認狀況下,REPL只能解釋一行語句,可是if表達式一般須要放在多行。

    ·可使用{}的方式,好比如下方式,或者使用:pastectrl+D的方式。

if(age > 18) { "adult"

} else if(age > 12) "teenager" else "children"

語句終結符、塊表達式

·默認狀況下,scala不須要語句終結符,默認將每一行做爲一個語句

·一行放多條語句:若是一行要放多條語句,則必須使用語句終結符

    ·例如,使用分號做爲語句終結符,var a, b, c = 0; if(a < 10) { b = b + 1; c = c + 1 }

    ·一般來講,對於多行語句,仍是會使用花括號的方式

if(a < 10) {

    b = b + 1

    c = c + 1

}

·塊表達式:塊表達式,指的就是{}中的值,其中能夠包含多條語句,最後一個語句的值就是塊表達式的返回值。

    ·例如,var d = if(a < 10) { b = b + 1; c + 1 }

輸入和輸出

·printprintlnprint打印時不會加換行符,而println打印時會加一個換行符。

    ·例如,print("Hello World"); println("Hello World")

·printfprintf能夠用於進行格式化

    ·例如,printf("Hi, my name is %s, I'm %d years old.\n", "Leo", 30)

·readLine: readLine容許咱們從控制檯讀取用戶輸入的數據,相似於java中的System.inScanner的做用。

·綜合案例:遊戲廳門禁

val name = readLine("Welcome to Game House. Please tell me your name: ")

print("Thanks. Then please tell me your age: ")

val age = readInt()

if(age > 18) {

  printf("Hi, %s, you are %d years old, so you are legel to come here!", name, age)

} else {

  printf("Sorry, boy, %s, you are only %d years old. you are illegal to come here!", name, age)

}

循環

·while do循環:Scalawhile do循環,基本語義與Java相同。

var n = 10

while(n > 0) {

  println(n)

  n -= 1

}

·Scala沒有for循環,只能使用while替代for循環,或者使用簡易版的for語句

    ·簡易版for語句:var n = 10; for(i <- 1 to n) println(i)

    ·或者使用until,表式不達到上限:for(i <- 1 until n) println(i)

    ·也能夠對字符串進行遍歷,相似於java的加強for循環,for(c <- "Hello World") print(c)

·跳出循環語句

    ·scala沒有提供相似於javabreak語句。

    ·可是可使用boolean類型變量、return或者Breaksbreak函數來替代使用。

import scala.util.control.Breaks._

breakable {

    var n = 10

    for(c <- "Hello World") {

        if(n == 5) break;

        print(c)

        n -= 1

    }

}

高級for循環

·多重for循環:九九乘法表

for(i <- 1 to 9; j <- 1 to 9) {

  if(j == 9) {

    println(i * j)

  } else {

    print(i * j + " ")

  }

}

·if守衛:取偶數

for(i <- 1 to 100 if i % 2 == 0) println(i)

·for推導式:構造集合

for(i <- 1 to 10) yield i

 

函數的定義與調用

Scala中定義函數時,須要定義函數的函數名、參數、函數體。

咱們的第一個函數以下所示:

def sayHello(name: String, age: Int) = {

  if (age > 18) { printf("hi %s, you are a big boy\n", name); age }

  else { printf("hi %s, you are a little boy\n", name); age

}

sayHello("leo", 30)

Scala要求必須給出全部參數的類型,可是不必定給出函數返回值的類型,只要右側的函數體中不包含遞歸的語句,Scala就能夠本身根據右側的表達式推斷出返回類型。

在代碼塊中定義包含多行語句的函數體

單行的函數:def sayHello(name: String) = print("Hello, " + name)

若是函數體中有多行代碼,則可使用代碼塊的方式包裹多行代碼,代碼塊中最後一行的返回值就是整個函數的返回值。與Java中不一樣,不是使用return返回值的。

好比以下的函數,實現累加的功能:

def sum(n: Int) = {

  var sum = 0;

  for(i <- 1 to n) sum += i

  sum

}

遞歸函數與返回類型

若是在函數體內遞歸調用函數自身,則必須手動給出函數的返回類型。

例如,實現經典的斐波那契數列:

9 + 8; 8 + 7 + 7 + 6; 7 + 6 + 6 + 5 + 6 + 5 + 5 + 4; ....

def fab(n: Int): Int = {

  if(n <= 1) 1

  else fab(n - 1) + fab(n - 2)

}

默認參數

Scala中,有時咱們調用某些函數時,不但願給出參數的具體值,而但願使用參數自身默認的值,此時就定義在定義函數時使用默認參數。

def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName

若是給出的參數不夠,則會從左往右依次應用默認參數。

def sayHello(name: String, age: Int = 20) {

  print("Hello, " + name + ", your age is " + age)

}

sayHello("leo")

函數調用時帶名參數

在調用函數時,也能夠不按照函數定義的參數順序來傳遞參數,而是使用帶名參數的方式來傳遞。

sayHello(firstName = "Mick", lastName = "Nina", middleName = "Jack")

還能夠混合使用未命名參數和帶名參數,可是未命名參數必須排在帶名參數前面。

sayHello("Mick", lastName = "Nina", middleName = "Jack")

變長參數

Scala中,有時咱們須要將函數定義爲參數個數可變的形式,則此時可使用變長參數定義函數。

def sum(nums: Int*) = {

  var res = 0

  for (num <- nums) res += num

  res

}

sum(1, 2, 3, 4, 5)

序列做爲變長參數

在若是想要將一個已有的序列直接調用變長參數函數,是不對的。好比val s = sum(1 to 5)。此時須要使用Scala特殊的語法將參數定義爲序列,讓Scala解釋器可以識別。這種語法很是有用!必定要好好主意,在spark的源碼中大量地使用到了。

val s = sum(1 to 5: _*)

案例:使用遞歸函數實現累加

def sum2(nums: Int*): Int = {

  if (nums.length == 0) 0

  else nums.head + sum2(nums.tail: _*)

}

過程

Scala中,定義函數時,若是函數體直接包裹在了花括號裏面,而沒有使用=鏈接,則函數的返回值類型就是Unit。這樣的函數就被稱之爲過程,即過程就是沒有返回值的函數。

過程還有一種寫法,就是將函數的返回值類型定義爲Unit

def sayHello(name: String) = "Hello, " + name//函數

def sayHello(name: String) { print("Hello, " + name); "Hello, " + name }//有值,但未使用=號,仍是過程

def sayHello(name: String): Unit = "Hello, " + name//有值,有=號,但強制返回類型爲空,則仍是過程

lazy

Scala中,提供了lazy值的特性,也就是說,若是將一個變量聲明爲lazy,則只有在第一次使用該變量時,變量對應的表達式纔會發生計算。這種特性對於特別耗時的計算操做特別有用,好比打開文件進行IO,進行網絡IO等。

 

import scala.io.Source._

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

即便文件不存在,也不會報錯,只有第一個使用變量時會報錯,證實了表達式計算的lazy特性。

 

val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

至關於定義了一個方法,只有在調用該方法時纔會去執行方法體:

def lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString

異常

Scala中,異常處理和捕獲機制與Java是很是類似的。

try {

  throw new IllegalArgumentException("x should not be negative")

} catch {

  case _: IllegalArgumentException => println("Illegal Argument!")

} finally {

  print("release resources!")

}

 

Import java.io._

try {

  throw new IOException(「io exception!!!")

} catch {

  case _: IllegalArgumentException => println("illegal argument")

}

 

try {

  throw new IOException("user defined exception")

} catch {

  case e1: IllegalArgumentException => println("illegal argument")

  case e2: IOException => println("io exception")

}

Array

Scala中,Array表明的含義與Java中相似,也是長度不可改變的數組。此外,因爲ScalaJava都是運行在JVM中,雙方能夠互相調用,所以Scala數組的底層其實是Java數組。例如字符串數組在底層就是JavaString[],整數數組在底層就是JavaInt[]

// 數組初始化後,長度就固定下來了,並且元素所有根據其類型初始化

val a = new Array[Int](10)

a(0)

a(0) = 1

val a = new Array[String](10)

// 能夠直接使用Array()建立數組,元素類型自動推斷

val a = Array("hello", "world")

a(0) = "hi"

val a = Array("leo", 30)

ArrayBuffer

Scala中,若是須要相似於Java中的ArrayList這種長度可變的集合類,則可使用ArrayBuffer

// 若是不想每次都使用全限定名,則能夠預先導入ArrayBuffer

import scala.collection.mutable.ArrayBuffer

// 使用ArrayBuffer()的方式能夠建立一個空的ArrayBuffer

val b = ArrayBuffer[Int]()

// 使用+=操做符,能夠添加一個元素,或者多個元素

// 這個語法必需要謹記在心!由於spark源碼裏大量使用了這種集合操做語法!

b += 1

b += (2, 3, 4, 5)

// 使用++=操做符,能夠添加其餘集合中的全部元素

b ++= Array(6, 7, 8, 9, 10)

// 使用trimEnd()函數,能夠從尾部截斷指定個數的元素

b.trimEnd(5)

 

// 使用insert()函數能夠在指定位置插入元素

// 可是這種操做效率很低,由於須要移動指定位置後的全部元素

b.insert(5, 6)

b.insert(6, 7, 8, 9, 10)

// 使用remove()函數能夠移除指定位置的元素

b.remove(1)

b.remove(1, 3)

// ArrayArrayBuffer能夠互相進行轉換

b.toArray

a.toBuffer

遍歷ArrayArrayBuffer

// 使用for循環和until遍歷Array / ArrayBuffer

// 使untilRichInt提供的函數

for (i <- 0 until b.length)

  println(b(i))

// 跳躍遍歷Array / ArrayBuffer

for(i <- 0 until (b.length, 2))

  println(b(i))

// 從尾部遍歷Array / ArrayBuffer

for(i <- (0 until b.length).reverse)

  println(b(i))

// 使用加強for循環遍歷Array / ArrayBuffer

for (e <- b)

  println(e)

數組常見操做

// 數組元素求和

val a = Array(1, 2, 3, 4, 5)

val sum = a.sum

// 獲取數組最大值

val max = a.max

// 對數組進行排序

scala.util.Sorting.quickSort(a)

// 獲取數組中全部元素內容

a.mkString

a.mkString(", ")

a.mkString("<", ",", ">")

// toString函數

a.toString

b.toString

使用yield和函數式編程轉換數組

// Array進行轉換,獲取的仍是Array

val a = Array(1, 2, 3, 4, 5)

val a2 = for (ele <- a) yield ele * ele

// ArrayBuffer進行轉換,獲取的仍是ArrayBuffer

val b = ArrayBuffer[Int]()

b += (1, 2, 3, 4, 5)

val b2 = for (ele <- b) yield ele * ele

// 結合if守衛,僅轉換須要的元素

val a3 = for (ele <- if ele % 2 == 0) yield ele * ele

// 使用函數式編程轉換數組(一般使用第一種方式)

a.filter(_ % 2 == 0).map(2 * _)

a.filter { _ % 2 == 0 } map { 2 * _ }

算法案例:移除第一個負數以後的全部負數

// 構建數組

val a = ArrayBuffer[Int]()

a += (1, 2, 3, 4, 5, -1, -3, -5, -9)

// 每發現一個第一個負數以後的負數,就進行移除,性能較差,屢次移動數組

var foundFirstNegative = false

var arrayLength = a.length

var index = 0

while (index < arrayLength) {

  if (a(index) >= 0) {

    index += 1

  } else {

    if (!foundFirstNegative) { foundFirstNegative = true; index += 1 }

    else { a.remove(index); arrayLength -= 1 }

  }

}

算法案例:移除第一個負數以後的全部負數(改良版)

// 從新構建數組

val a = ArrayBuffer[Int]()

a += (1, 2, 3, 4, 5, -1, -3, -5, -9)

// 每記錄全部不須要移除的元素的索引,稍後一次性移除全部須要移除的元素

// 性能較高,數組內的元素遷移只要執行一次便可

var foundFirstNegative = false

val keepIndexes = for (i <- 0 until a.length if !foundFirstNegative || a(i) >= 0) yield {

  if (a(i) < 0) foundFirstNegative = true

  i

}

for (i <- 0 until keepIndexes.length) { a(i) = a(keepIndexes(i)) }

a.trimEnd(a.length - keepIndexes.length)

建立Map

// 建立一個不可變的Map

val ages = Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)

ages("Leo") = 31

// 建立一個可變的Map

val ages = scala.collection.mutable.Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)

ages("Leo") = 31

// 使用另一種方式定義Map元素

val ages = Map(("Leo", 30), ("Jen", 25), ("Jack", 23))

// 建立一個空的HashMap

val ages = new scala.collection.mutable.HashMap[String, Int]

訪問Map的元素

// 獲取指定key對應的value,若是key不存在,會報錯

val leoAge = ages("Leo")

val leoAge = ages("leo")

// 使用contains函數檢查key是否存在

val leoAge = if (ages.contains("leo")) ages("leo") else 0

// getOrElse函數

val leoAge = ages.getOrElse("leo", 0)

修改Map的元素

// 更新Map的元素

ages("Leo") = 31

// 增長多個元素

ages += ("Mike" -> 35, "Tom" -> 40)

// 移除元素

ages -= "Mike"

// 更新不可變的map

val ages2 = ages + ("Mike" -> 36, "Tom" -> 40)

// 移除不可變map的元素

val ages3 = ages - "Tom"

遍歷Map

// 遍歷mapentrySet

for ((key, value) <- ages) println(key + " " + value)

// 遍歷mapkey

for (key <- ages.keySet) println(key)

// 遍歷mapvalue

for (value <- ages.values) println(value)

// 生成新map,反轉keyvalue

for ((key, value) <- ages) yield (value, key)

SortedMapLinkedHashMap

// SortedMap能夠自動對Mapkey的排序

val ages = scala.collection.immutable.SortedMap("leo" -> 30, "alice" -> 15, "jen" -> 25)

// LinkedHashMap能夠記住插入entry的順序

val ages = new scala.collection.mutable.LinkedHashMap[String, Int]

ages("leo") = 30

ages("alice") = 15

ages("jen") = 25

Map的元素類型—Tuple

// 簡單Tuple

val t = ("leo", 30)

// 訪問Tuple

t._1

// zip操做

val names = Array("leo", "jack", "mike")

val ages = Array(30, 24, 26)

val nameAges = names.zip(ages)

for ((name, age) <- nameAges) println(name + ": " + age)

定義一個簡單的類

// 定義類,包含field以及方法

class HelloWorld {

  private var name = "leo"

  def sayHello() { print("Hello, " + name) } 

  def getName = name

}

// 建立類的對象,並調用其方法

val helloWorld = new HelloWorld

helloWorld.sayHello()

print(helloWorld.getName) // 也能夠不加括號,若是定義方法時不帶括號,則調用方法時也不能帶括號

gettersetter

1、定義不帶private var field,此時scala生成class時,會自動生成一個private[this]的成員字段(名稱與field不一樣),並還生成一對gettersetter方法,分別叫作field field_=,而且gettersetter方法的訪問修飾符與field定義相同

2、而若是使用private修飾field,則只生成的gettersetter,且訪問修飾也是private

3、若是定義val field,則只會生成getter方法

4 若是不但願生成settergetter方法,則將field聲明爲private[this]

class Student {

  var name = "leo"

}

// 調用gettersetter方法,分別叫作namename_=

val leo = new Student

print(leo.name)

leo.name = "leo1" //實際上會調用 leo.name_=("leo1")方法

自定義gettersetter

// 若是隻是但願擁有簡單的gettersetter方法,那麼就按照scala提供的語法規則,根據需求爲field選擇合適的修飾符就好:varvalprivateprivate[this]

// 可是若是但願可以本身對gettersetter進行控制,則能夠自定義gettersetter方法

// 自定義setter方法的時候必定要注意scala的語法限制,簽名、=、參數間不能有空格

class Student {

  private var myName = "leo" //默認會生成一對private getter(myName)setter(myName _=)方法

  def name = "your name is " + myName //自定義myName 成員變量getter方法

  def name_=(newValue: String)  {//自定義myName 成員變量的setter方法

    print("you cannot edit your name!!!")

  }

}

val leo = new Student

print(leo.name)

leo.name = "leo1" //會去調用 name_+ 自定義setter 方法

僅暴露fieldgetter方法

// 若是你不但願fieldsetter方法,則能夠定義爲val,可是此時就不再能更改field的值了

// 可是若是但願可以僅僅暴露出一個getter方法,而且還能經過某些方法更改field的值,那麼須要綜合使用private以及自定義getter方法。此時,因爲fieldprivate的,因此settergetter都是private,對外界沒有暴露;本身能夠實現修改field值的方法;本身能夠覆蓋getter方法

class Student {

  private var myName = "leo"

  def updateName(newName: String) { //更改field的其餘方法(命名約束的不知足setter方法)

    if(newName == "leo1") myName = newName

    else print("not accept this new name!!!")

  }

  def name = "your name is" + myName  //覆蓋自動生成的私有getter方法

}

private[this]的使用

// 若是將field使用private來修飾,那麼表明這個field是類私有的,在類的方法中,能夠直接訪問類的其餘對象的private field

// 這種狀況下,若是不但願field被其餘對象訪問到,那麼可使用private[this],意味着對象私有的field,只有本對象內能夠訪問到(子類對象中也是不能夠訪問的,由於私有的是不能被繼承的)

class Student {

  private var myAge = 0 //試着修改爲private[this]

  def age_=(newValue: Int) {

    if (newValue > 0) myAge = newValue

    else print("illegal age!")

  }

  def age = myAge

  def older(s: Student) = {

    myAge > s.myAge //修改爲private[this]後,就會報錯

  }

}

private[this]還能夠用爲修改方法

Java風格的gettersetter方法

// Scalagettersetter方法的命名與java是不一樣的,是fieldfield_=的方式

// 若是要讓scala自動生成java風格的gettersetter方法,只要給field添加@BeanProperty註解便可

// 此時會生成4個方法,name: Stringname_=(newValue: String): UnitgetName(): StringsetName(newValue: String): Unit

import scala.reflect.BeanProperty

class Student {

  @BeanProperty var name: String = _

}

class Student(@BeanProperty var name: String)

val s = new Student

s.setName("leo")

s.getName()

輔助constructor

// Scala中,能夠給類定義多個輔助constructor,相似於java中的構造函數重載

// 輔助constructor之間能夠互相調用,並且必須第一行調用主constructor或其餘輔構造器

class Student {

  private var name = ""

  private var age = 0

  def this(name: String) {

    this()

    this.name = name

  }

  def this(name: String, age: Int) {

    this(name)

    this.age = age

  }

}

constructor

// Scala中,主constructor是與類名放在一塊兒的,有且只有一個,與java不一樣

// 並且類中,沒有定義在任何方法中的代碼(包括成員字段),都屬於主constructor的代碼,且執行的順序與代碼書寫的順序一致。這其實與Java是同樣的,在Java中方法以外的代碼(成員以及代碼塊)會在構造器調用以前最早執行,姑且將這些代碼看做也是放到了一個主構造器中進行執行的,只不過這種主構造器不能帶構造參數

//主構造器與類定義是在一塊兒的,若是有參數,則在類名後面跟括號便可:

class Student(val name: String, val age: Int) {

  println("your name is " + name + ", your age is " + age)

}

固然沒有參數的主構造器也能夠帶括號:

class Student() {}

 

// constructor中還能夠經過使用默認參數,來給參數默認的值

class Student(val name: String = "leo", val age: Int = 30) {

  println("your name is " + name + ", your age is " + age)

}

// 若是主constrcutor傳入的參數什麼修飾都沒有,好比name: String,那麼若是類內部除主constrcutor方法外其它方法也使用到了,則會自動將該參數修飾爲private[this] name以便其它方法使用:

class Student(name: String) {

         def f(){print(name)}

         def f(s:Student){print(s.name)}//編譯出錯

}

class Student(val name: String) {

         def f(){print(name)}

         def f(s:Student){print(s.name)}//編譯正確,證實沒有使用varval修飾時,且在除主構造器中使用外,則使用private[this]來修飾

}

類中沒有定義的在任何方法中的代碼都屬於主構造器,而且執行順序與書寫順序一致:

內部類

// Scala中,一樣能夠在類中定義內部類;可是與java不一樣的是,每一個外部類的對象的內部類,都屬於不一樣的類

import scala.collection.mutable.ArrayBuffer

class Class {

  class Student(val name: String) {}

  val students = new ArrayBuffer[Student]

  def getStudent(name: String) =  {

    new Student(name)

  }

}

val c1 = new Class

val s1 = c1.getStudent("leo")

c1.students += s1

val c2 = new Class

val s2 = c2.getStudent("leo")

c1.students += s2   //異常

object

1object,至關於class的單個實例(但與從class實例化出來的對象的內容決不同),一般在裏面放一些class層面上共享的內容,如Java中的靜態field或者method即在定義在object中(注:Scala中沒有Java的靜態概念,因此延伸出了object這個東東)

2、你能夠將object看做是一個類class,只是這個類在內存中只有一個單例,且定義的object名就是實例名,不需咱們本身實例化,運行時JVM已幫咱們new出來了

3、第一次調用object的方法時,就會執行objectconstructor,也就是object內部不在method中的代碼;可是object不能定義接受參數的constructor

4、注意,objectconstructor只會在其第一次被調用時執行一次,之後再次調用就不會再次執行constructor

5object一般用於做爲單例模式的實現,或者放class的靜態成員,好比工具方法

object Person {

  private var eyeNum = 2

  println("this Person object!")

  def getEyeNum = eyeNum

}

伴生對象

// 若是有一個class,還有一個與class同名的object,那麼就稱這個objectclass的伴生對象,classobject的伴生類

// 伴生類和伴生對象必須存放在一個.scala文件之中

// 伴生類和伴生對象,最大的特色就在於,互相能夠訪問private field

object Person {

  private val eyeNum = 2

  def getEyeNum = eyeNum

}

class Person(val name: String, val age: Int) {

  def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.")

}

object繼承抽象類

// object的功能其實和class相似,除了不能定義接受參數的constructor以外

// object也能夠繼承抽象類,並覆蓋抽象類中的方法

abstract class Hello(var message: String) {

  def sayHello(name: String): Unit

}

object HelloImpl extends Hello("hello") {

  override def sayHello(name: String) = {

    println(message + ", " + name)

  }

}

apply方法

// object中很是重要的一個特殊方法,就是apply方法

// 一般在伴生對象中實現apply方法,並在其中實現構造伴生類的對象的功能,通常用做工廠方法

// 而建立伴生類的對象時,一般不會使用new Class的方式,而是使用Class()的方式,隱式地調用伴生對象得apply方法,這樣會讓對象建立更加簡潔

// 好比,Array類的伴生對象的apply方法就實現了接收可變數量的參數,並建立一個Array對象的功能

val a = Array(1, 2, 3, 4, 5)

// 好比,定義本身的伴生類和伴生對象

class Person(val name: String)

object Person {

  def apply(name: String) = new Person(name)

}

 

另外,若是直接在一個對象後面接小括號,則會去調用這個對象所對應類中相應的apply方法:

class Person {

  def apply(name: String) = println(name)

}

scala> val p = new Person

scala> p("Persion")

Persion

main方法

// 就如同java中,若是要運行一個程序,必須編寫一個包含main方法類同樣;在scala中,若是要運行一個應用程序,那麼必須有一個main方法,做爲入口

// scala中的main方法定義爲def main(args: Array[String]),並且必須定義在object

object HelloWorld {

  def main(args: Array[String]) {

    println("Hello World!!!")

  }

}

// 除了本身實現main方法以外,還能夠繼承App Trait,而後將須要在main方法中運行的代碼,直接做爲objectconstructor代碼;並且用args能夠接受傳入的參數

object HelloWorld extends App {

  if (args.length > 0) println("hello, " + args(0))

  else println("Hello World!!!")

}

 

// 若是要運行上述代碼,須要將其放入.scala文件,而後先使用scalac編譯,再用scala執行

scalac HelloWorld.scala

scala -Dscala.time HelloWorld

// App Trait的工做原理爲:App Trait繼承自DelayedInit Traitscalac命令進行編譯時,會把繼承App Traitobjectconstructor代碼都放到DelayedInit TraitdelayedInit方法中,而後由App Traitmain方法去調用執行

object來實現枚舉功能

// Scala沒有直接提供相似於Java中的Enum這樣的枚舉特性,若是要實現枚舉,則須要用object繼承Enumeration類,而且調用Value方法來初始化枚舉值

object Season extends Enumeration {

  val SPRING, SUMMER, AUTUMN, WINTER = Value

}

// 還能夠經過Value傳入枚舉值的idname,經過idtoString能夠獲取; 還能夠經過idname來查找枚舉值

object Season extends Enumeration {

  val SPRING = Value(0, "spring")

  val SUMMER = Value(1, "summer")

  val AUTUMN = Value(2, "autumn")

  val WINTER = Value(3, "winter")

}

Season(0)  // spring

Season.withName("spring") // spring,根據名稱找

// 使用枚舉object.values能夠遍歷枚舉值

for (ele <- Season.values) println(ele)

extends

// Scala中,讓子類繼承父類,與Java同樣,也是使用extends關鍵字

// 繼承就表明,子類能夠從父類繼承父類的fieldmethod;而後子類能夠在本身內部放入父類所沒有,子類特有的fieldmethod;使用繼承能夠有效複用代碼

// 子類能夠覆蓋父類的fieldmethod;但要注意的是final類是不能被繼承的,並且final類型的fieldmethod是沒法被覆蓋的

class Person {

  private var name = "leo"

  def getName = name

}

class Student extends Person {

  private var score = "A"

  def getScore = score

}

overridesuper

// Scala中,若是子類要覆蓋一個父類中的非抽象方法,則必須使用override關鍵字;若是是抽象的方法,則能夠省略

// override關鍵字能夠幫助咱們儘早地發現代碼裏的錯誤,好比:override修飾的父類方法的方法名咱們拼寫錯了;好比要覆蓋的父類方法的參數咱們寫錯了;等等

// 此外,在子類覆蓋父類方法以後,若是咱們在子類中就是要調用父類的被覆蓋的方法呢?那就可使用super關鍵字,顯式地指定要調用父類的方法

class Person {

  private var name = "leo"

  def getName = name

}

class Student extends Person {

  private var score = "A"

  def getScore = score

  override def getName = "Hi, I'm " + super.getName

}

重寫時須要override關鍵字,若是是實現則能夠省略override關鍵字

override field

子類能夠覆蓋父類的同名的非private成員

// Scala中,子類能夠覆蓋父類的val field,並且子類的val field還能夠覆蓋父類的val fieldgetter方法;只要在子類中使用override關鍵字便可

class Person {

  /*private*/ val name: String = "Person"

}

class Student extends Person {

  override val name: String = "leo" // 重寫必定要帶上override關鍵字

}

 

只有val變量才能被重寫var變量是不能被重寫的

class A{

         var f = "a"

}

class B extends A{

         override var f = "b" // error: overriding variable f in class A of type String;

                                             //variable f cannot override a mutable variable

}

下面也是不行的:

class A{

         var f:String = "a"

}

class B extends A{

         override def f:String = "b"

         override def f_=(x:String) = println(x) // error: overriding variable f in class A of type String;

                                                                            //method f_= cannot override a mutable variable

}

 

var變量只能被實現,若是將上面換成抽象的var字段,則是能夠的:

abstract class A{

         var f:String

}

class B extends A{

         /* override */ def f:String = "b" //因爲是實現,因此能夠省略override

         override def f_=(x:String) = println(x) //也能夠不省略override

}

或者:

abstract class A{

         var f:String

}

class B extends A{

         var f:String = ""

}

 

val變量只能被val實現,不能被def實現:

abstract class A{

         val f:String

}

class B extends A{

         def f:String = ""  // error: overriding value f in class A of type String;

                                             //method f needs to be a stable, immutable value

}

但能夠這樣:

abstract class A{

         val f:String

}

class B extends A{

         val f:String = ""

}

valvar override/實現 def

abstract class Person {

    def id: Int  

}

class Student extends Person{

    override var id = 9527  //Error: method id_= overrides nothing

}

scala中定義了一個var變量,會自動生成gettersetter方法。因爲父類中只定義了一個方法def id: Int,而子類中var變量會自動生成getterid)與setter方法(id_),可是父類並無這個setter方法,因此是沒法重寫的。以下修改便可:

abstractclass Person  {
def idInt    :   
def id_=Int//父類必須有set方法    (value: ) 
}
class Student extends Person{
overridevar9527//var變量自動生成getset方法      id =  
}

或者子類定義成val變量:

abstractclass Person  {
def idInt    :   
}
class Student extends Person{
overrideval9527      id = 
}
 
上面是valvar來實現def,下面是valvar來重寫def
class Person {
    def id: Int = 1
}
class Student extends Person{
    override val id = 9527 
}
class Person {
    def id: Int = 1
    def id_=(value: Int) =println(value) 
}
class Student extends Person{
    override var id = 9527 
}
 
可是不能使用def重寫valvar
class Person {
  val sex: String 
}
class Student extends Person {
   override def sex:String = "" //error: overriding value sex in class Person of type String;
                                      //method sex needs to be a stable, immutable value
}
 
class Person {
  var sex: String = "X"
}
class Student extends Person {
  override def sex:String = "" 
  override def sex_=(x:String) = println(x)
}
不能使用def實現val:
abstract class Person {
  val sex: String 
}
class Student extends Person {
   def sex:String = "" //error: overriding value sex in class Person of type String;
                               //method sex needs to be a stable, immutable value
}
可使用def實現var
abstract class Person {
  var sex: String 
}
class Student extends Person {
  def sex:String = ""
  def sex_=(x:String) = println(x)
}
 
成員變量與方法之間重寫與實現結論:可使用valvar來重寫或實現def,也可使用def實現var;但不能使用def重寫valvar,也不能使用def實現val

isInstanceOfasInstanceOf

// 若是咱們建立了子類的對象,可是又將其賦予了父類類型的變量。則在後續的程序中,咱們又須要將父類類型的變量轉換爲子類類型的變量,應該如何作?

// 首先,須要使用isInstanceOf判斷對象是不是指定類的對象,若是是的話,則可使用asInstanceOf將對象轉換爲指定類型

// 注意,若是對象是null,則isInstanceOf必定返回falseasInstanceOf必定返回null

// 注意,若是沒有用isInstanceOf先判斷對象是否爲指定類的實例,就直接用asInstanceOf轉換,則可能會拋出異常

class Person

class Student extends Person

val p: Person =  new Student

var s: Student = null

if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]

 

scala> p.isInstanceOf[Student]

res7: Boolean = true

scala> p.isInstanceOf[Person]

res8: Boolean = true

getClassclassOf

// isInstanceOf只能判斷出對象是不是給定類或其子類的實例對象,而不能精確判斷出對象就是給定類的實例對象

// 若是要求精確地判斷對象就是指定類的對象,那麼就只能使用getClassclassOf

// 對象.getClass能夠精確獲取對象所屬的類classclassOf[]能夠精確獲取類,而後使用==操做符便可判斷

class Person

class Student extends Person

val p: Person = new Student

p.isInstanceOf[Person]

p.getClass == classOf[Person]

p.getClass == classOf[Student]

使用模式匹配進行類型判斷

// 可是在實際開發中,好比spark的源碼中,大量的地方都是使用了模式匹配的方式來進行類型的判斷,這種方式更加地簡潔明瞭,並且代碼得可維護性和可擴展性也很是的高

// 使用模式匹配,功能性上來講,與isInstanceOf同樣,也是判斷主要是該類以及該類的子類的對象便可,也不是精準判斷的

class Person

class Student extends Person

val p: Person = new Student

p match {

  case per: Person => println("it's Person's object")

  case _  => println("unknown type")

}

protected

// java同樣,scala中一樣可使用protected關鍵字來修飾fieldmethod,這樣子類就能夠繼承這些成員或方法

// 還可使用protected[this],則只能在當前子類對象中訪問父類的使用protected[this]修飾的fieldmethod,沒法經過其餘子類對象訪問父類中的這些字段與方法

class Person {

  protected var name: String = "leo"

  protected[this] var hobby: String = "game"

}

class Student extends Person {

  def sayHello = println("Hello, " + name)

  def makeFriends(s: Student) {

    println("my hobby is " + hobby + ", your hobby is " + s.hobby) //此處編譯出錯

  }

}

protected[this]修飾的字段只能在本對象或其子對象中使用,不能在其餘對象中使用:

class Person {

  protected var name: String = "leo"

  protected[this] var hobby: String = "game"

  def makeFriends(s: Person ){

    println("my hobby is " + hobby + ", your hobby is " + s.hobby) //此處編譯仍是出錯

  }

}

private[this]同樣,protected[this]也能夠修飾方法

調用父類的constructor

// Scala中,每一個類能夠有一個主constructor和任意多個輔助constructor,而每一個輔助constructor的第一行都必須是調用其餘輔助constructor或者是主constructor;所以子類的輔助constructor是必定不可能直接調用父類的constructor

// 只能在子類的constructor中調用父類的constructor,如下這種語法,就是經過子類的主構造函數來調用父類的構造函數(即在extends後面指定須要調用父類哪一個構造器)

// 注意!若是是父類中接收的參數,好比nameage,子類中接收時,就不要用任何valvar來修飾了(或者帶上修飾了,但將參數名命成不同也可),不然會認爲是子類要覆蓋父類的field

class Person(val name: String, val age: Int)

class Student(name: String, age: Int, var score: Double) extends Person(name, age) /*調用父類的輔助構造器*/{

  def this(name: String) {

    this(name, 0, 0) //調用主構造器

  }

  def this(age: Int) {

    this("leo", age, 0) //調用主構造器

  }

}

調用父類的主構造器:

class Person(val name: String, val age: Int){

         def this(){

                   this("11",11)

         }

}

class Student(name: String, age: Int, var score: Double) extends Person/* Person()*/ {

  def this(name: String) {

    this(name, 0, 0) //調用主構造器

  }

  def this(age: Int) {

    this("leo", age, 0) //調用主構造器

  }

}

匿名內部類

// Scala中,匿名子類是很是常見,並且很是強大的。Spark的源碼中也大量使用了這種匿名子類。

// 匿名子類,也就是說,能夠定義一個類的沒有名稱的子類,並直接建立其對象,而後將對象的引用賦予一個變量。以後甚至能夠將該匿名子類的對象傳遞給其餘函數使用。

class Person(protected val name: String) {

  def sayHello = "Hello, I'm " + name

}

val p = new Person("leo") {

  override def sayHello = "Hi, I'm " + name

}

def greeting(p: Person { def sayHello: String }) {

  println(p.sayHello)

}

抽象類

// 若是在父類中,有某些方法沒法當即實現,而須要依賴不一樣的子來來覆蓋,重寫實現本身不一樣的方法實現。此時能夠將父類中的這些方法不給出具體的實現,只有方法簽名,這種方法就是抽象方法。

// 而一個類中若是有一個抽象方法,那麼類就必須用abstract來聲明爲抽象類,此時抽象類是不能夠實例化的

// 在子類中覆蓋抽象類的抽象方法時,不須要使用override關鍵字(也可帶上),但若是是重寫父類具體方法或成員,則不能省略override

abstract只能修飾類,不能修飾成員與方法,哪怕成員(沒有初始化)與方法(沒有方法體)是抽象的

 

abstract class Person(val name: String) {

  def sayHello: Unit

}

class Student(name: String) extends Person(name) {

  def sayHello: Unit = println("Hello, " + name)

}

抽象field

// 若是在父類中,定義了field,可是沒有給出初始值,則此field爲抽象field

// 抽象field意味着,scala會根據本身的規則,爲varval類型的field生成對應的gettersetter方法,可是父類中是沒有該field

// 子類必須覆蓋field,以定義本身的具體field,而且覆蓋抽象field,不須要使用override關鍵字

abstract class Person {

  val name: String

}

class Student extends Person {

  val name: String = "leo"

}

 

 

沒有初始化的成員所在的類要是抽象類:

abstract class A{

         var a:String

}

 

/*class B extends A*/編譯時報錯:須要重寫父類的抽象成員

class B extends A{

         /*override*/ var a:String = "a" //也能夠省略override

}

除了經過上面直接覆蓋父類的抽象成員外,還能夠簡接經過實現抽象成員所對應的gettersetter方法便可:

class B extends A{

         /*override*/ def a = "a" //因爲是實現,因此能夠省略override

         override def a_=(x:String){println(a)}

}

上面是經過實現父類抽象成員所對應的gettersetter方法來重寫抽象成員,因此能夠看出:沒有被初始化的成員所對應的gettersetter方法實質上就是抽象的,因此類要定義是abstract,成員字段自己沒有什麼抽象不抽象的概念

trait做爲接口使用

// Scala中的Triat是一種特殊的概念

// 首先咱們能夠將Trait做爲接口來使用,此時的Triat就與Java中的接口很是相似

// triat中能夠定義抽象方法,就與抽象類中的抽象方法同樣,只要不給出方法的具體實現便可

// 類可使用extends關鍵字繼承trait,注意,這裏不是implement,而是extends,在scala中沒有implement的概念,不管繼承類仍是trait,統一都是extends

// 類繼承trait後,必須實現其中的抽象方法(若是是trait繼承trait則不須要,這比如Java中的接口繼承接口同樣),實現時不須要使用override關鍵字

// scala不支持對類進行多繼承,可是支持多重繼承trait,使用with關鍵字便可

trait HelloTrait {

  def sayHello(name: String)

}

trait MakeFriendsTrait {

  def makeFriends(p: Person)

}

class Person(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable {

  def sayHello(name: String) = println("Hello, " + name)

  def makeFriends(p: Person) = {sayHello(name);println("Hello, my name is " + name + ", your name is " + p.name)}

}

val p1 = new Person("leo")

val p2 = new Person("lily")

p1.makeFriends(p2)

Trait中定義具體方法

// Scala中的Triat能夠不是隻定義抽象方法,還能夠定義具體方法,此時trait更像是包含了通用工具方法的東西
//
有一個專有的名詞來形容這種狀況,就是說trait的功能混入了類

// 舉例來講,trait中能夠包含一些不少類都通用的功能方法,好比打印日誌等等,spark中就使用了trait來定義了通用的日誌打印方法

trait Logger {

  def log(message: String) = println(message)

}

class Person(val name: String) extends Logger {

  def makeFriends(p: Person) {

    println("Hi, I'm " + name + ", I'm glad to make friends with you, " + p.name)

    log("makeFriends methdo is invoked with parameter Person[name=" + p.name + "]")

  }

}

val p1 = new Person("leo")

val p2 = new Person("lily")

p1.makeFriends(p2)

Trait中定義具體字段

// Scala中的Triat能夠定義具體field,此時繼承trait的類就自動得到了trait中定義的field

trait Person {

  val eyeNum: Int = 2

}

class Student(val name: String) extends Person {

  def sayHello = println("Hi, I'm " + name + ", I have " + eyeNum + " eyes.")

}

val s = new Student("leo")

s.sayHello

Trait中定義抽象字段

// Scala中的Triat能夠定義抽象field,而trait中的具體方法則能夠基於抽象field來編寫

// 可是繼承trait的類,則必須覆蓋抽象field,提供具體的值

trait SayHello {

  val msg: String //抽象字段

  def sayHello(name: String) = println(msg + ", " + name) // 具體方法調用抽象字段(實質上是調用val抽象字段所對應的getter抽象方法),至關於Java中的模式方法,另參看這裏

}

class Person(val name: String) extends SayHello {

  val msg: String = "hello"

  def makeFriends(p: Person) {

    sayHello(p.name)

    println("I'm " + name + ", I want to make friends with you!")

  }

}

val p1 = new Person("leo")

val p2 = new Person("lily")

p1.makeFriends(p2)

爲實例混入trait

// 有時咱們能夠在建立類的對象時,指定該對象混入某個trait,這樣,就只有這個對象混入該trait的方法,而類的其餘對象則沒有

trait Logged {

  def log(msg: String) {}

}

trait MyLogger extends Logged {

  override def log(msg: String) { println("log: " + msg) }

class Person(val name: String) extends Logged {

    def sayHello { println("Hi, I'm " + name); log("sayHello is invoked!") }

}

val p1 = new Person("leo")

p1.sayHello  // Hi, I'm leo

val p2 = new Person("jack") with MyLogger //實例化時混入

p2.sayHello  // Hi, I'm jack

//log: sayHello is invoked!

trait調用鏈

// Scala中支持讓類繼承多個trait後,依次調用多個trait中的同一個方法,只要讓多個trait的同一個方法中,在方法最後都執行「super.方法」來調用父類方法便可

// 類中調用多個trait中都有的這個方法時,首先會從最右邊的trait的方法開始執行,而後依次往左執行,造成一個調用鏈條

// 這種特性很是強大,其實就至關於設計模式中的責任鏈模式的一種具體實現

trait Handler {

  def handle(data: String) {}

}

trait DataValidHandler extends Handler {

  override def handle(data: String) {

    println("check data: " + data)

    super.handle(data)

  }

}

trait SignatureValidHandler extends Handler {

  override def handle(data: String) {

    println("check signature: " + data)

    super.handle(data)

  }

}

class Person(val name: String) extends SignatureValidHandler with DataValidHandler {

  def sayHello = { println("Hello, " + name); handle(name) }

}

val p = new Person("leo")

p.sayHello

 

Hello, leo

check data: leo

check signature: leo

trait中覆蓋抽象方法

// trait中,是能夠覆蓋父trait的抽象方法的

// 可是覆蓋時,若是使用了「super.方法」形式調用了父類抽象方法,則沒法經過編譯。由於super.方法就會去掉用父trait的抽象方法,此時子trait的該方法仍是會被認爲是抽象的,因此在override的同時還須要加上abstract

// 此時若是要經過編譯,就得給子trait的方法加上abstract override修飾

trait Logger {

  def log(msg: String)

}

trait MyLogger extends Logger {

  abstract override def log(msg: String) { println("MyLogger.log()");super.log(msg) }

}

class BasicLog extends Logger{

def log(msg: String) { println("BasicLog.log()"); println(msg) }

}

class Person(val name: String) extends BasicLog with MyLogger {

  def makeFriends(p: Person) {

    println("Hi, I'm " + name + ", I'm glad to make friends with you, " + p.name)

    log("makeFriends methdo is invoked with parameter Person[name=" + p.name + "]")

  }

}

val p1 = new Person("leo")

val p2 = new Person("lily")

p1.makeFriends(p2)

 

Hi, I'm leo, I'm glad to make friends with you, lily

MyLogger.log()

BasicLog.log()

makeFriends methdo is invoked with parameter Person[name=lily]

混合使用trait的具體方法和抽象方法

// trait中,能夠混合使用具體方法和抽象方法

// 可讓具體方法依賴於抽象方法,而抽象方法則放到繼承trait的類中去實現

// 這種trait其實就是設計模式中的模板設計模式的體現

trait Valid { 

  def getName: String  //抽象方法

  def valid: Boolean = { //具體方法中調用抽象方法,至關於Java中的模板方法

    getName == "leo"   

  }

}

class Person(val name: String) extends Valid {

  println(valid)

  def getName = name

}

val p = new Person("leo") //true

trait的構造機制

// Scala中,trait也是有構造代碼的,也就是trait中的,不包含在任何方法中的代碼

// 而繼承了trait的類的構造機制以下:1、父類的構造函數執行;2trait的構造代碼執行,多個trait左到右依次執行3、構造trait時會先構造父trait,若是多個trait繼承同一個父trait,則父trait只會構造一次4全部trait構造完畢以後,子類的構造函數執行

class Person { println("Person's constructor!") }

trait Logger { println("Logger's constructor!") }

trait MyLogger extends Logger { println("MyLogger's constructor!") }

trait TimeLogger extends Logger { println("TimeLogger's constructor!") }

class Student extends Person with MyLogger with TimeLogger {

  println("Student's constructor!")

}

val s = new Student

trait field的初始化

// Scala中,trait的構造函數是不能接參數的(包括主構造器與輔助構造器),即trait不能定義輔助構造器,這是traitclass的惟一區別,可是若是需求就是要trait可以對field進行初始化,該怎麼辦呢?只能使用Scala中很是特殊的一種高級特性——提早定義

trait SayHello {

  val msg: String

  println("1SayHello")

  println(msg.toString) // NullPointerException異常。因爲在調用msg成員字段時,發如今msg是被從新實現(或重寫,這裏爲實現),則會去調用子類中的實現的msg成員,但因爲此時子類構造器還未執行,因此子類msg還沒來得及初始化,因此返回null,最終致使空指針異常

}

class Person extends SayHello{

 println("2Person")

 val msg:String = "init"

}

new Person // NullPointerException異常,緣由父trait構造代碼會先於子類構造器執行,在執行msg.toString時子類中的msg尚未來得及初始化。但若是將上面的val都修改成def,則能夠正常運行。由於初始化父類時,因爲子類實現(或重寫,這裏爲實現)了msg方法,因此msg.toString會去調用子類實現的msg方法而返回"init",即便此時子類尚未被初始化:

trait SayHello {

  def msg: String

  println("1SayHello")

  println(msg.toString)

}

class Person extends SayHello{

 println("2Person")

 def msg:String = "init"

}

new Person

 

即便父類提供了初始化,但仍是拋NullPointerException,緣由是子類重寫了父類該字段msg,在執行父類構造器中的msg.toString時,msg使用的是子類中被重寫過的,但此時子類構造器還未被執行,因此子類的msg此時還爲null

trait SayHello {

  val msg: String = "000"

  println("1SayHello")

  println(msg.toString) // NullPointerException

}

class Person extends SayHello{

 println("2Person")

 override val msg:String = "init"

}

new Person

而下面的則不會拋異常了,緣由是子類沒有重寫msg字段,因此父類構造器在執行時,msg使用的仍是父中的msg,且已經被初始化過了:

trait SayHello {

  val msg: String = "000"

  println("1SayHello")

  println(msg.toString) // 不會拋異常,注意:此名要放在上面msg初始化語句的後面,不然仍是會拋空指針

}

class Person extends SayHello{

 println("2Person")

}

new Person

 

下面根據前面的知識(字段與方法相互實現與重寫),結合上面的經驗,分析分析一下下面的狀況:

如下也能夠,緣由也是經過方法的多態來初始化:

trait SayHello {

  var msg: String

  println("1SayHello")

  println(msg.toString)//會去調用子類實現方法msg,順利執行

}

class Person extends SayHello{

 println("2Person")

 def msg:String = {println("person.msg");"init" }

 def msg_=(x:String) = println(x)

}

new Person

 

trait SayHello {

  var msg: String

  println("1SayHello")

  println(msg.toString) // NullPointerException,緣由父類中的msg被子類實現過,但父類調用時,子類還未初始msg字段

}

class Person extends SayHello{

 println("2Person")

 var msg:String = "init"

}

new Person

上面除了經過調用子類實現(或重寫)方法解決問題外,下面還能夠經過提早定義方式來初始化:

trait SayHello {

  val msg: String

  println("3SayHello")

  println(msg.toString)

}

class Person{println("2Person")}

val p = new {

  val msg: String = {println("1init");"init"}  // 實例化時提早初始化

} with Person with SayHello

1init -> 2Person -> 3SayHello

注意上面new … withclass …extends…with的區別,new…with是動態混入,執行構造器是從new後面的類(或塊,這裏爲塊)開始從左到右依次執行;而class…extends…with則是靜態混入,在定義class時就已肯定,其構造器是從extends後面的類開始從左往右依次執行,執行完後最後執行class 後面指定的類的構造器。以下面的new … with形式構造順序:

trait A{

 println("a")

}

class B extends A{

 println("b")

}

trait C extends A{

 println("c")

}

new B with C // a -> b -> c

 

class…extends…with構造順序:

trait A{

 println("a")

}

trait B{

 println("b")

}

class C extends A with B{

 println("c")

}

new C // a -> b -> c

 

下面是另外一種初始化方式(class …extends…with靜態定義方式),此種方式比上面初始化方式好理解一點:

trait SayHello {

  val msg: String

  println("2SayHello")

  println(msg.toString)

}

class Person extends {

  val msg: String = {println("1init");"init"} // 類定義時提早初始化

} with SayHello {

  println("3Person")

}

new Person

 

// 另一種方式就是使用lazy value

trait SayHello {

  lazy val msg: String = {println("SayHello");null} // 此句不會執行

  println(msg.toString) // 此句會調用子類重寫過的msg成員,因爲子類msg定義成了lazy,而lazy變量有個特性就是在使用時會執行右邊表達式,因此在這裏調用msg.toString方法時,就會觸發懶加載右邊的計算表達式,因此lazy字段不是由類來初始化的,而是由調用時機來決定,因此子類中的lazy msg會先於子類其餘成員被初始化

  println("2")

}

class Person extends SayHello {

  println("3")

  val m: String = {println("4");"m"}

  override lazy val msg: String = {println("1");"init"}

}

new Person

 

1

init

2

3

4

trait繼承class

// Scala中,trait也能夠繼承自class,此時這個class就會成爲全部繼承該trait的類的父類

class MyUtil {

  def printMessage(msg: String) = println(msg)

}

trait Logger extends MyUtil {

  def log(msg: String) = printMessage("log: " + msg)

}

class Person(val name: String) extends Logger {

  def sayHello {

    log("Hi, I'm " + name)

    printMessage("Hi, I'm " + name)

  }

}

new Person("leo").sayHello

 

log: Hi, I'm leo

Hi, I'm leo

將函數賦值給變量

// Scala中的函數是一等公民,能夠獨立定義,獨立存在,並且能夠直接將函數做爲值賦值給變量

// Scala的語法規定,將函數賦值給變量時,必須在函數後面加上空格和下劃線

def sayHello(name: String) { println("Hello, " + name) }

val sayHelloFunc = sayHello _

sayHelloFunc("leo")

匿名函數

// Scala中,函數也能夠不須要命名,此時函數被稱爲匿名函數。

// 能夠直接定義函數以後,將函數賦值給某個變量;也能夠將直接定義的匿名函數傳入其餘函數之中

// Scala定義匿名函數的語法規則就是,(參數名: 參數類型) => 函數體

// 這種匿名函數的語法必須深入理解和掌握,在spark的中有大量這樣的語法,若是沒有掌握,是看不懂spark源碼的

val sayHelloFunc = (name: String) => println("Hello, " + name)

sayHelloFunc("leo")

 

變量帶返回類型:

val sayHelloFunc:String=>Unit = (name: String) => println("Hello, " + name)

高階函數

// Scala中,因爲函數是一等公民,所以能夠直接將某個函數傳入其餘函數,做爲參數。這個功能是極其強大的,也是Java這種面向對象的編程語言所不具有的。

// 接收其餘函數做爲參數的函數,也被稱做高階函數(higher-order function

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)

 

// 高階函數的另一個功能是將函數做爲返回值,即返回值就是一個函數,以下面根據不一樣的msg生成不一樣的函數

def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)

var greetingFunc = getGreetingFunc("hello")

greetingFunc("leo")

greetingFunc = getGreetingFunc("hi")

greetingFunc("leo")

高階函數的類型推斷

// 高階函數能夠自動推斷出參數類型,而不須要寫明類型;並且對於只有一個參數的函數,還能夠省去其小括號;

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")

 

// 只要某個參數只在函數體裏出現一次,則可使用下劃線 _ 來替換這個參數

def triple(func: (Int) => Int) = { func(3) }

triple(3 * _)

 

// 諸如3 * _的這種語法,必須掌握!!spark源碼中大量使用了這種語法!

 

有多少個下劃線,則就表示有多少個不一樣的參數。多個佔位符時,第一個下劃線表示第一個參數,第二個下劃線表示第二個參數,以此類推;因此同一參數多處出現時是沒法使用這種佔位符來表示的。

 

使用佔位符時,有時沒法推導出類型,如:

scala> val f = _ + _

此時需明確寫出類型:

scala> val f = (_: Int) + (_: Int)

f: (Int, Int) => Int = <function2>

Scala的經常使用高階函數

// map: 對傳入的每一個元素都進行映射,返回一個處理後的元素

Array(1, 2, 3, 4, 5).map(2 * _)

 

// foreach: 對傳入的每一個元素都進行處理,可是沒有返回值

(1 to 9).map("*" * _).foreach(println _)

 

// filter: 對傳入的每一個元素都進行條件判斷,若是對元素返回true,則保留該元素,不然過濾掉該元素

(1 to 20).filter(_ % 2 == 0)

 

// reduceLeft: 從左側元素開始,進行reduce操做,即先對元素1和元素2進行處理,而後將結果與元素3處理,再將結果與元素4處理,依次類推,即爲reducereduce操做必須掌握!spark編程的重點!!!

// 下面這個操做就至關於1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9

(1 to 9).reduceLeft( _ * _)

 

// sortWith: 對元素進行兩兩相比,進行排序

Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)

閉包

// 閉包最簡潔的解釋:函數在變量不處於其有效做用域時,還可以對變量進行訪問,即爲閉包

def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)

val greetingFuncHello = getGreetingFunc("hello")

val greetingFuncHi = getGreetingFunc("hi")

// 兩次調用getGreetingFunc函數,傳入不一樣的msg,並建立不一樣的函數返回

// 然而,msg只是一個局部變量,卻在getGreetingFunc執行完以後,還能夠繼續存在建立的函數之中;greetingFuncHello("leo"),調用時,值爲"hello"msg被保留在了函數體內部,能夠反覆的使用

// 這種變量超出了其做用域,還可使用的狀況,即爲閉包

// Scala經過爲每一個函數建立對象來實現閉包,實際上對於getGreetingFunc函數建立的函數,msg是做爲函數對象的變量存在的,所以每一個函數才能夠擁有不一樣的msg

SAM轉換

若是Scala調用Java的某個方法傳入的是一個SAM,則能夠經過Scala提供的很方便的一種轉換,將函數對象會傳給Java方法

 

// Java中,因爲不支持直接將函數傳入一個方法做爲參數,一般來講,惟一的辦法就是定義一個實現了某個接口的類的實例對象,該對象只有一個方法,猶如這樣接口只有單個的抽象方法,就叫single abstract method,簡稱爲SAM

 

// 因爲Scala是能夠調用Java的代碼的,所以當咱們調用Java的某個方法時,可能就不得不建立SAM傳遞給方法,很是麻煩;可是Scala又是支持直接傳遞函數的。此時就可使用Scala提供的,在調用Java方法時,使用Scala提供的SAM轉換功能,即將SAM轉換爲Scala函數

 

// 要使用SAM轉換,須要使用Scala提供的特性,隱式轉換

import javax.swing._

import java.awt.event._

val button = new JButton("Click")

button.addActionListener(new ActionListener {// ActionListener接口只有一個抽象方法,這樣的接口叫SAM

  override def actionPerformed(event: ActionEvent) {

    println("Click Me!!!")

  }

})

 

implicit def getActionListener(actionProcessFunc: (ActionEvent) => Unit) = new ActionListener {

  override def actionPerformed(event: ActionEvent) {

    actionProcessFunc(event)

  }

}

button.addActionListener((event: ActionEvent) => println("Click Me!!!"))

Currying函數

// Curring函數,指的是,將原來接收兩個參數的一個函數,轉換爲兩個函數,第一個函數接收原先的第一個參數,而後返回接收原先第二個參數的第二個函數。

// 在函數調用的過程當中,就變爲了兩個函數連續調用的形式

// Spark的源碼中,也有體現,因此對()()這種形式的Curring函數,必須掌握!

def sum(a: Int, b: Int) = a + b

sum(1, 1)

 

def sum2(a: Int) = (b: Int) => a + b

sum2(1)(1)

 

def sum3(a: Int)(b: Int) = a + b

sum2(1)(1)

return到外層函數

// Scala中,不須要使用return來返回函數的值,函數最後一行語句的值,就是函數的返回值。在Scala中,return用於在匿名函數中返回值給包含匿名函數的帶名函數(即外層函數),並做爲帶名函數的返回值。

// 使用return的匿名函數,是必須給出返回類型的,不然沒法經過編譯

def greeting(name: String) = {

  def sayHello(name: String):String = {

    return "Hello, " + name

  }

  sayHello(name)

}

greeting("leo")

Scala的集合體繫結構

// Scala中的集合體系主要包括:IterableSeqSetMap。其中Iterable是全部集合trait的根trait。這個結構與Java的集合體系很是類似(最上層爲public interface Collection<E> extends Iterable<E>)。

// Scala中的集合是分紅可變和不可變兩類集合的,其中可變集合就是說,集合的元素能夠動態修改,而不可變集合的元素在初始化以後,就沒法修改了。分別對應scala.collection.mutablescala.collection.immutable兩個包。

// Seq下包含了RangeArrayBufferList等子trait。其中Range就表明了一個序列,一般可使用「1 to 10」這種語法來產生一個Range ArrayBuffer就相似於Java中的ArrayList

List

// List表明一個不可變的列表

// List的建立,val list = List(1, 2, 3, 4)

// Listheadtailhead表明List的第一個元素,tail表明第一個元素以後的全部元素,list.headlist.tail

// List有特殊的::操做符,能夠用於將headtail合併成一個List0 :: list

// ::這種操做符要清楚,在spark源碼中都是有體現的,必定要可以看懂!

// 若是一個List只有一個元素,那麼它的head就是這個元素,它的tailNil

// 案例:用遞歸函數來給List中每一個元素都加上指定前綴,並打印加上前綴的元素

def decorator(l: List[Int], prefix: String) {

  if (l != Nil) {

    println(prefix + l.head)

    decorator(l.tail, prefix)

  }

}

LinkedList

// LinkedList表明一個可變的列表,使用elem能夠引用其頭部,使用next能夠引用其尾部

// val l = scala.collection.mutable.LinkedList(1, 2, 3, 4, 5); l.elem; l.next

// 案例:使用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

}

// 案例:使用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

}

Set

// Set表明一個沒有重複元素的集合,Settrait,分爲可變與不可變兩種trait

// 將重複元素加入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](); i += 1; s += 2; s += 5

// SrotedSet會自動根據key來進行排序,val s = scala.collection.mutable.SortedSet("orange", "apple", "banana")

集合的函數式編程

// 集合的函數式編程很是很是很是之重要!!!

// 必須徹底掌握和理解Scala的高階函數是什麼意思,Scala的集合類的mapflatMapreducereduceLeftforeach等這些函數,就是高階函數,由於能夠接收其餘函數做爲參數

// 高階函數的使用,也是ScalaJava最大的一點不一樣!!!由於Java裏面是沒有函數式編程的,也確定沒有高階函數,也確定沒法直接將函數傳入一個方法,或者讓一個方法返回一個函數

// Scala高階函數的理解、掌握和使用,能夠大大加強你的技術,並且也是Scala最有誘惑力、最有優點的一個功能!!!

// 此外,在Spark源碼中,有大量的函數式編程,以及基於集合的高階函數的使用!!!因此必須掌握,才能看懂spark源碼

 

// map案例實戰:爲List中每一個元素都添加一個前綴

List("Leo", "Jen", "Peter", "Jack").map("name is " + _)

 

// faltMap案例實戰:將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))

函數式編程綜合案例:統計多個文本內的單詞總數

// 使用scalaio包將文本文件內的數據讀取出來

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編程的精髓所在,就是函數式編程,也是Scala相較於Java等編程語言最大的功能優點所在

// 並且,spark的源碼中大量使用了這種複雜的鏈式調用的函數式編程

// 並且,spark自己提供的開發人員使用的編程api的風格,徹底沿用了Scala的函數式編程,好比Spark自身的api中就提供了mapflatMapreduceforeach,以及更高級的reduceByKeygroupByKey等高階函數

// 若是要使用Scala進行spark工程的開發,那麼就必須掌握這種複雜的高階函數的鏈式調用!!!

 

lines.flatMap(_.split(" ")).map((_, 1)).map(_._2).reduceLeft(_ + _)

模式匹配

// Scala是沒有Java中的switch case語法的,相對應的,Scala提供了更增強大的match case語法,即模式匹配,類替代switch casematch case也被稱爲模式匹配

 

// Scalamatch caseJavaswitch case最大的不一樣點在於,Javaswitch case僅能匹配變量的值,比123等;而Scalamatch case能夠匹配各類狀況,好比變量的類型、集合的元素、有值或無值

 

// match case的語法以下:變量 match { case => 代碼 }。若是值爲下劃線,則表明了不知足以上全部狀況下的默認狀況如何處理。此外,match case中,只要一個case分支知足並處理了,就不會繼續判斷下一個case分支了。(與Java不一樣,javaswitch 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")

  }

}

在模式匹配中使用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")

  }

}

在模式匹配中進行變量賦值

// Scala的模式匹配語法,有一個特色在於,能夠將模式匹配的默認狀況,將下劃線替換爲一個變量名,此時模式匹配語法就會將要匹配的值賦值給這個變量,從而能夠在後面的處理語句中使用要匹配的值

 

// 爲何有這種語法??思考一下。由於只要使用用case匹配到的值,是否是咱們就知道這個只啦!!在這個case的處理語句中,是否是就直接可使用寫程序時就已知的值!

 

// 可是對於下劃線_這種狀況,全部不知足前面的case的值,都會進入_這種默認狀況進行處理,此時若是咱們在處理語句中須要拿到具體的值進行處理呢?那就須要使用這種在模式匹配中進行變量賦值的語法!!

 

// 案例:成績評價(升級版)

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+  " : " + grade_)

    case _ => println("you need to work harder, your grade is " + grade)

  }

}

對類型進行模式匹配

// Scala的模式匹配一個強大之處就在於,能夠直接匹配類型,而不是值!!!這點是javaswitch 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!" )

  }

}

processException(new IOException ("File not found"))

ArrayList進行模式匹配

// Array進行模式匹配,分別能夠匹配帶有指定元素的數組、帶有指定個數元素的數組、以某元素打頭的數組

// List進行模式匹配,與Array相似,可是須要使用List特有的::操做符

// 案例:對朋友打招呼

def greeting(arr: Array[String]) {

  arr match {

    case Array("Leo") => println("Hi, Leo!")

    case Array(girl1, girl2, girl3) => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)

    case Array("Leo", _*) => println("Hi, Leo, please introduce your friends to me.")

    case _ => println("hey, who are you?")

  }

}

greeting(Array("Leo","Jack"))

 

def greeting(list: List[String]) {

  list match {

    case "Leo" :: Nil => println("Hi, Leo!")

    case girl1 :: girl2 :: girl3 :: Nil => println("Hi, girls, nice to meet you. " + girl1 + " and " + girl2 + " and " + girl3)

    case "Leo" :: tail => println("Hi, Leo, please introduce your friends to me.")

    case _ => println("hey, who are you?")

  }

}

greeting(List("Leo","Jack"))

case class與模式匹配

// Scala中提供了一種特殊的類,用case class進行聲明,中文也能夠稱做樣例類。case class其實有點相似於Java中的JavaBean的概念。即只定義field,而且由Scala編譯時自動提供gettersetter方法,可是沒有method

 

// case class的主構造函數接收的參數一般不須要使用varval修飾Scala自動就會使用val修飾(可是若是你本身使用var修飾,那麼仍是會按照var來)

 

//  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!")

  } 

}

judgeIdentify(Student("Leo","1"))

Option與模式匹配

// Scala有一種特殊的類型,叫作OptionOption有兩種值,一種是Some,表示有值,一種是None,表示沒有值。

// Option一般會用於模式匹配中,用於判斷某個變量是有值仍是沒有值,這比null來的更加簡潔明瞭

// Option的用法必須掌握,由於Spark源碼中大量地使用了Option,好比Some(a)None這種語法,所以必須看得懂Option模式匹配,纔可以讀懂spark源碼。

// 案例:成績查詢

val grades = Map("Leo" -> "A", "Jack" -> "B", "Jen" -> "C")

def getGrade(name: String) {

  val grade = grades.get(name)

  grade match {

    case Some(grade1) => println("your grade is " + grade1)

    case None => println("Sorry, your grade information is not in the system")

  }

}

getGrade("Lily")

getGrade("Leo")

 

Scala集合類的某些標準操做會產生Option可選值,如Mapget方法,查到值時返回Some(value)對象,沒查到時返回None對象(而Java中返回的爲Null,這會容易致使程序運行錯誤)

類型參數

類型參數是什麼?類型參數其實就相似於Java中的泛型。先說說Java中的泛型是什麼,好比咱們有List a = new ArrayList(),接着a.add(1),沒問題,a.add("2"),而後咱們a.get(1) == 2,對不對?確定不對了,a.get(1)獲取的實際上是個String——"2"String——"2"怎麼可能與一個Integer類型的2相等呢?

 

因此Java中提出了泛型的概念,其實也就是類型參數的概念,此時能夠用泛型建立ListList a = new ArrayList[Integer](),那麼,此時a.add(1)沒問題,而a.add("2")呢?就不行了,由於泛型會限制,只能往集合中添加Integer類型,這樣就避免了上述的問題。

 

那麼Scala的類型參數是什麼?其實意思與Java的泛型是同樣的,也是定義一種類型參數,好比在集合,在類,在函數中,定義類型參數,而後就能夠保證使用到該類型參數的地方,就確定,也只能是這種類型。從而實現程序更好的健壯性。

 

此外,類型參數是Spark源碼中很是常見的,所以一樣必須掌握,才能看懂spark源碼。

泛型類

// 泛型類(類聲明時類名後面中括號中的即爲類型參數),顧名思義,其實就是在類的聲明中,定義一些泛型類型,而後在類內部,好比field或者method,就可使用這些泛型類型。

// 使用泛型類,一般是須要對類中的某些成員,好比某些fieldmethod中的參數或變量,進行統一的類型限制,這樣能夠保證程序更好的健壯性和穩定性。

// 若是不使用泛型進行統一的類型限制,那麼在後期程序運行過程當中,不免會出現問題,好比傳入了不但願的類型,致使程序出問題。

// 在使用類的時候,好比建立類的對象,將類型參數替換爲實際的類型,便可。

案例:新生報到,每一個學生來自不一樣的地方,id多是Int,多是String

class Student[T](val localId: T) { // 在類參數中使用類型參數

  def getSchoolId(hukouId: T) = "S-" + hukouId + "-" + localId // 在方法參數中使用類型參數

}

val leo = new Student[Int](111)

 

// Scala自動推斷泛型類型特性:直接給使用了泛型類型的field賦值時,Scala會自動進行類型推斷。

scala> val leo = new Student(111)

leo: Student[Int] = Student@f001896

scala> val leo = new Student("string")

leo: Student[String] = Student@488eb7f2

泛型函數

// 泛型函數(方法聲明時方法名後面中括號中的即爲類型參數),與泛型類相似,能夠給某個函數在聲明時指定泛型類型,而後在函數體內,多個變量或者返回值之間,就可使用泛型類型進行聲明,從而對某個特殊的變量,或者多個變量,進行強制性的類型限制。

案例:卡片售賣機,能夠指定卡片的內容,內容能夠是String類型或Int類型

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

}

getCard[String]("hello world")

getCard[Double](0.01)

 

// 與泛型類同樣,你能夠經過給使用了泛型類型的變量傳遞值來讓Scala自動推斷泛型的實際類型,也能夠在調用函數時,手動指定泛型類型,上面就是在調用時手動在中括號中指定的,下面靠傳入值自動推斷:

scala> getCard ("hello world")

res2: String = card: this is your card, hello world

scala> getCard (0.01)

res3: String = card: 0.01

上邊界Bounds

// 在指定泛型類型的時候,有時,咱們須要對泛型類型的範圍進行界定,而不是能夠是任意的類型。好比,咱們可能要求某個泛型類型,它就必須是某個類的子類,這樣在程序中就能夠放心地調用泛型類型繼承的父類的方法,程序才能正常的使用和運行。此時就可使用上下邊界Bounds的特性。以下面沒有使用上邊界時,是不能調用類型參數相關方法的:

class Person(val name: String) {

  def makeFriends(p: Person) {}

}

class Party[T](p1: T, p2: T) {

 def play = p1.makeFriends(p2) // 編譯會出錯

}

 

// Scala的上下邊界特性容許泛型類型必須是某個類的子類,或者必須是某個類的父類

 

案例:在派對上交朋友

class Person(val name: String) {

  def sayHello = println("Hello, I'm " + name)

  def makeFriends(p: Person) {

    sayHello

    p.sayHello

  }

}

class Student(name: String) extends Person(name)

class Party[T <: Person](p1: T, p2: T) { // <:要求T必須是Person或其子類,因爲p1 類型爲Person或其子類Student,因此能夠調用父類中的方法

  def play = p1.makeFriends(p2)

}

 

val leo = new Student("Leo")

val lily = new Student("Lily")

new Party(leo,lily).play

下邊界Bounds

// 除了指定泛型類型的上邊界,還能夠指定下邊界,即指定泛型類型必須是某個類的父類

 

案例:領身份證(只能是本身或父親代領)

class Father(val name: String)

class Child(name: String) extends Father(name)

def getIDCard[R >: Child](person: R) { // R必須是Child的父類

  if (person.getClass == classOf[Child]) println("please tell us your parents' names.")

  else if (person.getClass == classOf[Father]) println("sign your name for your child's id card.")

  else println("sorry, you are not allowed to get id card.")

}

 

val f = new Father("Father")

val c = new Child("Child")

scala> getIDCard[Father](f)

sign your name for your child's id card.

scala> getIDCard[Child](c)

please tell us your parents' names.

 

scala> getIDCard(f) // 類型自動推斷

sign your name for your child's id card.

scala> getIDCard(c) // 類型自動推斷

please tell us your parents' names.

 

val s = new Student("Student")

scala> getIDCard(s)

sorry, you are not allowed to get id card.

 

注:下邊界與上邊界是不一樣的,上邊界是爲了能夠調用類型參數相應方法,而下邊界是爲了限制泛型類或泛型函數只適用於哪些類,而不是爲了調用類型參數相應方法。

View Bounds

// 上下邊界Bounds,雖然可讓一種泛型類型,支持有父子關係的多種類型。可是,在某個類與上下邊界Bounds指定的父子類型範圍內的類都沒有任何關係,則默認是確定不能接受的。

 

// 然而,View Bounds做爲一種上下邊界Bounds的增強版,支持能夠對類型進行隱式轉換,將指定的類型進行隱式轉換後,再判斷是否在邊界指定的類型範圍內

 

案例:跟小狗交朋友

class Person(val name: String) {

  def sayHello = println("Hello, I'm " + name)

  def makeFriends(p: Person) {

    sayHello

    p.sayHello

  }

}

class Student(name: String) extends Person(name)

class Dog(val name: String) { def sayHello = println("Wang, Wang, I'm " + name) }

 

implicit def dog2person(o: Object): Person =

       if(o.isInstanceOf[Dog]) {println("-D-");val _o = o.asInstanceOf[Dog]; new Person(_o.name){

              override def sayHello = _o.sayHello

       } }// 若是是狗,隱式的轉換爲人

       //注:即便是PersonStudent,也要將Object強轉成Person後返回,而不能直接將o返回,不然可能引發循環調用隱式轉換

       else if(o.isInstanceOf[Person]) {println("-P-");val _o = o.asInstanceOf[Person];_o} // 若是是人,不用轉換,只是強轉型後返回

       else {println("-O-");error(o.toString)} //其餘狀況返回Nothing

      

      

class Party[T <% Person](p1: T){ // <%表示T能夠是Person或其子類,或者是能夠通過隱式轉換後成爲Person或其子類的類

   def play() = {println(p1.name);println("--------------------")}

}

class Party2[T <% Person](p1: T,p2: T){

  def play() = {p1.makeFriends(p2);println("--------------------")}

}

 

val leo = new Person("Leo")

val lily = new Student("Lily")

new Party(leo).play()

new Party(lily).play()

val dog = new Dog("Dog")

new Party(dog).play()//發生隱式轉換

new Party2(lily,leo).play()

new Party2(dog,leo).play()//發生隱式轉換

new Party2(lily,dog).play()//發生隱式轉換

Context Bounds

// Context Bounds是一種特殊的Bounds,它會根據泛型類型的聲明,好比「T: 類型要求必須存在一個類型爲類型[T]」的隱式值(運行時Scala會幫咱們自動注入這個已存在的隱式值)。其實我的認爲,Context Bounds之因此叫Context,是由於它基於的是一種全局的上下文,須要使用到上下文中的隱式值以及注入。

 

案例:使用Scala內置的比較器比較大小

// Ordering[T]相似Java中的Comparator<T>比較器

class Calculator[T: Ordering] (val number1: T, val number2: T) {

  //運行時,Scala會在上下文中去找類型爲Ordering[T]的隱式值並注進來,因此調用該方法時不須要傳遞該參數值了

  def max(implicit order: Ordering[T]) = if(order.compare(number1, number2) > 0) number1 else number2

}

new Calculator(3,4).max

Manifest Context Bounds

// Scala中,若是要實例化一個泛型數組,就必須使用Manifest Context Bounds。也就是說,若是數組元素類型爲T的話,須要爲類或者函數定義[T: Manifest]泛型類型,這樣才能實例化Array[T]這種泛型數組。

 

案例:打包飯菜(一種食品打成一包)

class Meat(val name: String)

class Vegetable(val name: String)

 

def packageFood[T: Manifest] (food: T*) = {

  // 建立泛型數組Array[T]即數組中的元素類型要在運行時才能肯定,編譯時沒法肯定,元素類型爲動態

  val foodPackage = new Array[T](food.length)

  for(i <- 0 until food.length) foodPackage(i) = food(i)

  foodPackage

}

val gongbaojiding = new Meat("gongbaojiding")

val yuxiangrousi = new Meat("yuxiangrousi")

val shousiyangpai = new Meat("shousiyangpai")

val meatPackageFood = packageFood(gongbaojiding,yuxiangrousi,shousiyangpai)

meatPackageFood: Array[Meat] = Array(Meat@20b829d5, Meat@7c5f29c6, Meat@4baf997)

 

val qingcai = new Vegetable("qingcai")

val baicai = new Vegetable("baicai")

val huanggua = new Vegetable("huanggua")

val vegPackageFood = packageFood(qingcai,baicai,huanggua)

vegPackageFood: Array[Vegetable] = Array(Vegetable@583030bd, Vegetable@2ac3d530, Vegetable@2431050d)

協變和逆變

// Scala的協變和逆變是很是有特點的!徹底解決了Java中的泛型的一大缺憾!

// 舉例來講,Java中,若是有ProfessionalMaster的子類,那麼Card[Professionnal]是否是Card[Master]的子類?答案是:不是。所以對於開發程序形成了不少的麻煩。

// Scala中,只要靈活使用協變和逆變,就能夠解決Java泛型的問題。

 

案例:進入會場

class Master // 大師

class Professional extends Master // 專家,按理來講,大師是一種專家,應該是MasterProfessional子類纔對

 

// 大師以及專家的名片均可以進入會場

class Card[+T] (val name: String) // 協變:當類型B是類型A的子類型,則能夠認爲T[B]T[A]的子類

def enterMeet(card: Card[Master]) {

  println("welcome to have this meeting!")

}

// 若是去掉+加號,則Card[Professional]不能傳入到enterMeet方法

 

//專家級別及以上大師級別的名片就能夠進入會場

class Card[-T] (val name: String) // 逆變:當類型B是類型A的子類型,則反過來能夠認爲T[A]T[B]的子類型

// 要想父類也能夠傳進來,則要讓Card進行逆變,這樣Card[Master]就反過來成爲Card[Professional]的子類,因此就能傳進來了

def enterMeet(card: Card[Professional]) {

  println("welcome to have this meeting!")

}

Existential Type

// Scala裏,有一種特殊的類型參數,就是Existential Type,存在性類型。這種類型務必掌握是什麼意思,由於在spark源碼實在是太常見了!

Array[T] forSome { type T }

Array[_]

 

 

scala> def foo[T](x : Array[T]) = println(x.length)

foo: [T](x: Array[T])Unit

scala>  foo(Array[String]("foo", "bar", "baz"))

3

 

scala> def foo(x : Array[T] forSome { type T}) = println(x.length)

foo: (x: Array[_])Unit

scala> foo(Array[String]("foo", "bar", "baz"))

3

 

scala> def foo(x : Array[_]) = println(x.length)

foo: (x: Array[_])Unit

scala> foo(Array[String]("foo", "bar", "baz"))

3

 

scala> def foo(x : Array[T] forSome { type T <: CharSequence}) = x.foreach(y => println(y.length))

foo: (x: Array[_ <: CharSequence])Unit

scala> foo(Array[String]("foo", "bar", "baz"))

3

3

3

隱式轉換

Scala提供的隱式轉換和隱式參數功能,是很是有特點的功能。是Java等編程語言所沒有的功能。它能夠容許你手動指定,將某種類型的對象轉換成其餘類型的對象。經過這些功能,能夠實現很是強大,並且特殊的功能。

 

Scala的隱式轉換,其實最核心的就是定義隱式轉換函數,即implicit conversion function。定義的隱式轉換函數,只要在編寫的程序內引入,就會被Scala自動使用。Scala會根據隱式轉換函數的簽名,在程序中使用到隱式轉換函數接收的參數類型定義的對象時,會自動將其傳入隱式轉換函數,轉換爲另一種類型的對象並返回。這就是隱式轉換

 

隱式轉換函數叫什麼名字是無所謂的,由於一般不會由用戶手動調用,而是由Scala進行調用。可是若是要使用隱式轉換,則須要對隱式轉換函數進行導入。所以一般建議將隱式轉換函數的名稱命名爲「one2one」的形式。

 

Spark源碼中有大量的隱式轉換和隱式參數,所以必須精通這種語法。

 

// 要實現隱式轉換,只要程序可見的範圍內定義隱式轉換函數便可。Scala會自動使用隱式轉換函數。隱式轉換函數與普通函數惟一的語法區別就是,要以implicit開頭,並且最好要定義函數返回類型。

 

// 案例:特殊售票窗口(只接受特殊人羣,好比學生、老人等)

class SpecialPerson(val name: String)

class Student(val name: String)

class Older(val name: String)

implicit def object2SpecialPerson (obj: Object): SpecialPerson = {

  if (obj.getClass == classOf[Student]) { val stu = obj.asInstanceOf[Student]; new SpecialPerson(stu.name) }

  else if (obj.getClass == classOf[Older]) { val older = obj.asInstanceOf[Older]; new SpecialPerson(older.name) }

  else error(obj.toString)

}

var ticketNumber = 0

def buySpecialTicket(p: SpecialPerson) = {//只針對特殊人羣賣票,因此若是是學生與老人,則要先轉換爲特殊人羣

  ticketNumber += 1

  "T-" + ticketNumber

}

 

class Teacher(val name:String)

scala> val tom = new Teacher("tom")

scala> buySpecialTicket(tom)

java.lang.RuntimeException: Teacher@277f7dd3

  at scala.sys.package$.error(package.scala:27)

  at scala.Predef$.error(Predef.scala:144)

  at .object2SpecialPerson(<console>:17)

  ... 32 elided

 

scala> val s = new Student("student")

scala> val o = new Older("older")

scala> buySpecialTicket(s)

res1: String = T-1

scala> buySpecialTicket(o)

res2: String = T-2

使用隱式轉換增強現有類型

// 隱式轉換很是強大的一個功能,就是能夠在不知不覺中增強現有類型的功能(有點像Java中的裝飾模式)。也就是說,能夠爲某個類定義一個增強版的類,並定義互相之間的隱式轉換,從而讓源類在使用增強版的方法時,由Scala自動進行隱式轉換爲增強類,而後再調用該方法(如內置的Int 類的增強版 RichInt)。

 

// 案例:超人變身

class Man(val name: String)

class Superman(val name: String) {

  def emitLaser = println("emit a laster!") // 超人才有該方法

}

implicit def man2superman(man: Man): Superman = new Superman(man.name)

val leo = new Man("leo") // 普通人

leo.emitLaser // 調用不存在的方法時,會自動的先轉換爲超人

隱式轉換函數做用域與導入

// Scala默認有兩種找隱式轉換的方式:首先是從源類型或者目標類型,這兩類型的伴生對象中找隱式轉換函數;而後在當前程序做用域內找隱式轉換函數。

 

// 若是隱式轉換函數不在上述兩種狀況下的話,那麼就必須手動使用import語法引入某個包下的隱式轉換函數,好比import test._。一般建議,僅僅在須要進行隱式轉換的地方,好比某個函數或者方法內,用import導入隱式轉換函數,這樣能夠縮小隱式轉換函數的做用域,避免不須要的隱式轉換。

隱式轉換的發生時機

// 1、調用某個函數,可是給函數傳入的參數的類型,與函數定義的接收參數類型不匹配案例:特殊售票窗口

// 2、使用某個類型的對象,調用某個方法,而這個方法並不存在於該類型時(案例:超人變身

// 3、使用某個類型的對象,調用某個方法,雖然該類型有這個方法,可是傳給方法的參數類型與方法定義的接收參數的類型不匹配(案例:特殊售票窗口增強版)

//4、將一種類型賦值給另外一種類型時,若是類型不兼容時

 

// 案例:特殊售票窗口增強版

class TicketHouse {

  var ticketNumber = 0

  def buySpecialTicket(p: SpecialPerson) = {

    ticketNumber += 1

    "T-" + ticketNumber

  }

}

new TicketHouse().buySpecialTicket(new Student("student"))

隱式參數

// 所謂的隱式參數,指的是在函數或者方法中,定義一個用implicit修飾的參數,此時Scala會嘗試找到一個指定類型的,用implicit修飾的對象,即隱式值,並自動注入到該隱式參數。

 

// Scala會在兩個範圍內查找:一種是當前做用域內定義valvar隱式變量;一種是從隱式參數類型的伴生對象內找隱式值

 

// 案例:考試簽到

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.")

}

Actor

ScalaActor相似於Java中的多線程編程。可是不一樣的是,ScalaActor提供的模型與多線程有所不一樣。ScalaActor儘量地避免鎖和共享狀態,從而避免多線程併發時出現資源爭用的狀況,進而提高多線程編程的性能。此外,Scala Actor的這種模型還能夠避免死鎖等一系列傳統多線程編程的問題。

 

Spark中使用的分佈式多線程框架,是AkkaAkka也實現了相似Scala Actor的模型,其核心概念一樣也是Actor。所以只要掌握了Scala Actor,那麼在Spark源碼研究時,至少便可看明白Akka Actor相關的代碼。可是,換一句話說,因爲Spark內部有大量的Akka Actor的使用,所以對於Scala Actor也至少必須掌握,這樣才能學習Spark源碼。

Actor的建立、啓動和消息收發

// Scala提供了Actor trait來讓咱們更方便地進行actor多線程編程,就Actor trait就相似於Java中的ThreadRunnable同樣,是基礎的多線程基類和接口。咱們只要重寫Actor traitact方法,便可實現本身的線程執行體,與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)

      }

    }

  }

}

val helloActor = new HelloActor

helloActor.start()

helloActor ! "leo"

收發case class類型的消息

// ScalaActor模型與Java的多線程模型之間,很大的一個區別就是,Scala Actor自然支持線程之間的精準通訊;即一個actor能夠給其餘actor直接發送消息。這個功能是很是強大和方便的。

// 要給一個actor發送消息,須要使用「actor ! 消息的語法。在scala中,一般建議使用樣例類,即case class來做爲消息進行發送。而後在actor接收消息以後,可使用scala強大的模式匹配功能來進行不一樣消息的處理。

// 案例:用戶註冊登陸後臺接口

import scala.actors.Actor

case class Login(username: String, password: String)

case class Register(username: String, password: String)

class UserManageActor extends Actor {

  def act() {

    while (true) {

      receive {

        case Login(username, password) => println("login, username is " + username + ", password is " + password)

        case Register(username, password) => println("register, username is " + username + ", password is " + password)

      }

    }

  }

}

val userManageActor = new UserManageActor

userManageActor.start()

userManageActor ! Register("leo", "1234"); userManageActor ! Login("leo", "1234")

Actor之間互相收發消息

// 若是兩個Actor之間要互相收發消息,那麼scala的建議是,一個actor向另一個actor發送消息時,同時帶上本身的引用;其餘actor收到本身的消息時,直接經過發送消息的actor的引用,便可以給它回覆消息。

// 案例:打電話

import scala.actors.Actor

case class Message(content: String, sender: Actor)

class LeoTelephoneActor extends Actor {

  def act() {

    while (true) {

      receive {

        case Message(content, sender) => { println("leo telephone: " + content); sender ! "I'm leo, please call me after 10 minutes." }

      }

    }

  }

}

class JackTelephoneActor(val leoTelephoneActor: Actor) extends Actor {

  def act() {

    leoTelephoneActor ! Message("Hello, Leo, I'm Jack.", this)

    receive {

      case response: String => println("jack telephone: " + response)

    }

  }

}

val leoTel = new LeoTelephoneActor

val jackTel = new JackTelephoneActor(leoTel)

leoTel.start

jackTel.start

同步消息和Future

// 默認狀況下,消息都是異步的;可是若是但願發送的消息是同步的,即對方接受後,必定要給本身返回結果,那麼可使用!?的方式發送消息。即val reply = actor !? message

 

// 若是要異步發送一個消息,可是在後續要得到消息的返回值,那麼可使用Future。即!!語法。val future = actor !! messageval reply = future()

 

 

附件列表

相關文章
相關標籤/搜索