Scala編程詳解

1.7 條件控制與循環(huán)

scala沒有for循環(huán)年柠,只能使用while循環(huán)替代for循環(huán),或者使用簡易版for循環(huán)

var n=10;for (i <- 1 to 10) println(i)

或者使用until泼掠,左閉右開 for (i <- 1 until 10) println(i)

增強(qiáng)for循環(huán)

for (c <- "hello") print(i + " ")

跳出循環(huán)語句

scala沒有break語句

import scala.utils.control.Breaks._

多重for循環(huán)

for (i <- 1 to 9; j <- 1 to 9){
    if (j == 9){
        println(i*j)
    }
    else{
        print(i*j + " ")
    }
}

if 守衛(wèi)

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

for 推導(dǎo)式

for (i <- 10) yield i

輸入和輸出

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 legal to come here!", name, age)
}else{
    printf("Sorry, boy, %s, you are only %d years old. you are illegal to come here!", name, age)
}


1.9 函數(shù)入門

def sayHello(name:String) = printf("hello %s!", name)

def fab(n: Int): Int = {
  if (n <=0 ) 1
  else fab(n-1) + fab(n-2)
}

默認(rèn)參數(shù)

def sayHello(firstName: String, lastName: String = "xixi") = printf(firstName+" "+lastName)

帶名參數(shù)

sayHello(lastName = "hehe", firstName = "oh")可以不考慮參數(shù)順序

變長參數(shù)

def sum(nums: Int*) = {
  var result = 0
  for (num <- nums){
    result += num
  }
  result
}

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

<u>使用序列調(diào)用變長參數(shù)</u>**

// 遞歸
def sum(nums: Int*): Int = {          // Int*是變長參數(shù)
  if (nums.length == 0) 0
  else nums.head + sum2(nums.tail:_*) //nums.tail: WrappedArray(2,3,4,5)
}

val s = sum(1 to 5)     //錯(cuò)誤!!! 1 to 5類型是range
val s = sum(1 to 5:_*)  // res0: Int = 15

**過程 **

過程: 定義函數(shù)時(shí)函數(shù)體沒有用=連接鱼冀,返回值類型為Unit

def sayHello(name: String) {print(xxx)}
def sayHello(name: String) = "Hello, " +name
def sayHello(name: String): Unit = "Hello, " +name

Lazy值

Lazy值: 只有在第一次使用該變量時(shí)报破,變量對(duì)應(yīng)的表達(dá)式才會(huì)發(fā)生計(jì)算(RDD)

lazy val lines = fromFile(filePath).mkString

異常

try{
  throw new IllegalArgumentException("x should not be negative")
}catch{
  case _: IllegalArgumentException => print("sorry, error!")
}finally{
  print("release io resource")
}

1.13 數(shù)組操作

Array

scala中Array代表的含義與Java類似,是長度不可變的數(shù)組

scala數(shù)組的底層實(shí)際上是Java數(shù)組

val a = new Array[Int](10)     //初始化為0
val a = new Array[String](10)  //初始化為null
val a = Array("aa", 30)        // 類型為Any
a(0) = 1

ArrayBuffer

import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]()
b += 1
b += (2,3,4,5)      // 重要@拙睢!理卑! 加多個(gè)元素 b:ArrayBuffer(1,2,3,4,5)
b ++= Array(6,7,8)  // ++=操作符 可以添加其他集合中的所有元素 b:ArrayBuffer(1,2,3,4,5,6,7,8)
b.trimEnd(5)        // 從尾部開始數(shù)5個(gè)截?cái)?(1,2,3)
b.insert(1,6)       // 在位置1插入6 (從0開始數(shù)) 也可以同時(shí)插入多個(gè)數(shù)b.insert(1翘紊,6,7藐唠,8)
b.remove(1,3)       // 從位置1開始移除掉3個(gè)元素

Array與ArrayBuffer相互轉(zhuǎn)換

val a = b.toArray
val b = a.toBuffer

遍歷Array和ArrayBuffer

for (i <- 0 until b.length) println(b(i))  // 注意這里是until不能用to 否則下標(biāo)越界
// 跳躍遍歷
for (i <- 0 until (b.length, 2) println(b(i))  // 隔2個(gè)遍歷 0,2,4,6,8
// 從尾部遍歷
for (i <- (0 until b.length).reverse) println(b(i)) 
// 增強(qiáng)for循環(huán)
for (e <- b) println(e)

數(shù)組其他操作

val a = Array(1,2,3,4,5)
val sum = a.sum
val max = a.max
scala.util.Sorting.quickSort(a)
a.mkString()    // String = 12345
a.mkString(",") // String = 1,2,3,4,5
a.mkString("<",",",">") // String = <1,2,3,4,5>

數(shù)組轉(zhuǎn)換

// 1. 使用yield轉(zhuǎn)換 類型不變 Array轉(zhuǎn)換后還是Array 
val a = Array(1,2,3,4,5)
val a2 = for(e<-a) yield e*e // Array(1,4,9,16,25)
val b = ArrayBuffer[Int]()
b += (1,2,3,4,5)
val b2 = for (e <- b if e%2 == 0) yield e*e // b2: ArrayBuffer
// 2. 使用函數(shù)式編程轉(zhuǎn)換數(shù)組
 a.filter(_%2==0).map(2*_)
 a.filter{_%2==0}.map{2*_}

算法案例

// 移除第一個(gè)負(fù)數(shù)之后的所有負(fù)數(shù)
val a = ArrayBuffer[Int]()
a += (1,2,3,4,5,-1,-3,-7,-11)
var foundFirstNegative = false
var arrayLen = a.length
var index = 0
while(index<arrayLen){
  if (a(index)>=0){
    index +=1
  }
  else{
    if (!foundFirstNegative){foundFirstNegative = true;index += 1} // 第一個(gè)負(fù)數(shù)不移除
    else {a.remove(index);arrayLen -= 1}
  }
}

這個(gè)算法的問題在于帆疟,發(fā)現(xiàn)了一個(gè)負(fù)數(shù)就要remove一次,則整個(gè)數(shù)組往前移一位宇立,性能較差$o(n^2)$

// 優(yōu)化:記錄索引踪宠,一次性移除
val a = ArrayBuffer[Int]()
a += (1,2,3,4,5,-1,-3,-7,-11)
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的前面
a.trimEnd(a.length - keepIndexes.length) // 把后面的負(fù)數(shù)截?cái)?

1.16 Map與Tuple

創(chuàng)建Map

val ages = Map("Leo" -> 30, "Jen" -> 21)
ages("Leo") = 21  // 默認(rèn)為immutable 不可修改
val ages = scala.collenction.mutable.Map("Leo" -> 30, "Jen" -> 21)
// 另一種方式定義Map元素
val ages = Map(("Leo", 30),("Jen", 21))
// 創(chuàng)建一個(gè)空的HashMap,但不能創(chuàng)建一個(gè)空的Map
val ages = new scala.collection.mutable.HashMap[String, Int]

訪問Map元素

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

修改Map元素

ages("Leo") = 31
ages += ("Mike" -> 35, "Tom" -> 30)
ages -= "Mike"
// 更新immutable的map
val ages2 = ages + ("Mike" -> 35, "Tom" -> 30)  // ages2是可變的
val ages3 = ages - "Tom"
val ages2 = ages + ("Leo" -> 31)  //【注意】這里會(huì)自動(dòng)對(duì)原有元素Leo的值進(jìn)行修改妈嘹,而不是增加新的元素

遍歷Map元素

for ((key, value) <- ages) println(key, value)
for (key <- ages.keySet)   println(key)
for (value <- ages.values) println(value)
for ((key, value) <- ages) yield(value, key) // 反轉(zhuǎn)key和value

SortedMap和LinkedHashMap

// SortedMap可以自動(dòng)對(duì)Map的key排序
val ages = scala.collection.immutable.SortedMap("leo"->30, "alice"->15, "jen"->25)
ages.keySet // Set(alice, jen, leo)
// LinkedHashMap可以記住插入的順序
val ages = new scala.collection.mutable.LinkedHashMap[String, Int]()
ages("leo") = 30
ages("alice") = 21

Tuple

val t = ("leo", 30)
t._1 // 下標(biāo)從1開始
val names = Array("leo", "jack", "mike")
val ages = Array(30, 24, 26)
val nameAges = names.zip(ages) // Array((leo, 30), (jack, 24), (mike, 26)) 元素為Tuple
for ((name, age) <- nameAges) println(name +":"+ age)

1.17 面向?qū)ο缶幊?/h3>

定義一個(gè)簡單的類

class HelloWorld{
  private var name = "leo"
  def sayHello() = {println("hello! "+name)}
  def getName = name
}
val hw = new HelloWorld
hw.name      // 不行柳琢,因?yàn)槭莗rivate,只能在類里使用
hw.sayHello()
hw.getName   // 不能用getName()润脸,因?yàn)槎x時(shí)沒有()

getter與setter

// 定義不帶private的var field柬脸,此時(shí)scala生成的面向JVM的類時(shí),會(huì)定義為private的name字段毙驯,并提供public的getter和setter方法
// 如果使用private修飾field倒堕, 則生成的getter和setter也是private的
// 如果定義val field,則只會(huì)生成getter方法
// 如果不希望生成setter和getter方法爆价,則將field聲明為private[this]
class Student{
  var name = "leo" // private 自動(dòng)生成getter和setter
}
//調(diào)用getter和setter方法垦巴,分別叫做name和name_=
val leo = new Student
print(leo.name)
leo.name = "leo1" // 調(diào)用name_=方法重新set name

自定義getter與setter

class Student{
  private var myName = "leo"
  def name = "your name is "+myName
  def name_=(newValue: String){      // name_= 等號(hào)不能有空格
    print("\nyou cannot edit your name")
  }
}

僅暴露field的getter方法

class Student{
  private var myName = "leo"
  def updateName(newName: String){
    if(newName == "leo1") myName = newName
    else print("not accept this new name!")
  }
  def name = "your name is "+myName
}

private[this]的使用

class Student{
  private var myAge = 0
  def age_=(newValue: Int){
    if(newValue > 0) myAge = newValue
    else print("illegal age!")
  }
  def age = myAge
  def older(s: Student) = {  
    myAge > s.myAge
  }
}

val s1 = new Student
s1.age = 20
val s2 = new Student
s2.age = 25
s1.older(s2)

Java風(fēng)格的getter和setter 加上注解 @BeanProperty var name: String = _

protected

// 用protected修飾的field和method,在子類中不需要用super關(guān)鍵字铭段,可以直接訪問
// 還可以使用protected[this]骤宣,則只能在當(dāng)前子類對(duì)象中訪問父類的field和method,無法通過其他子類對(duì)象訪問父類的field和method
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) // error: value hobby is not a member of Student
  }
}

輔助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的代碼
class Student(val name: String, val age: Int){
  println("your name is "+name+", your age is "+age)
}
// 主構(gòu)造參數(shù)也可以有默認(rèn)參數(shù)
class Student(val name: String = "leo", val age: Int = 18){
  println("your name is "+name+", your age is "+age)
}
val s = new Student
  
// 如果主constructor傳入的參數(shù)沒有修飾活逆,如name: String,如果類內(nèi)部的方法使用到了拗胜,那么會(huì)聲明為private[this] name; 否則沒有該field蔗候,就只能被constructor代碼使用

調(diào)用父類的constructor

// scala中,每個(gè)類可以有一個(gè)主constructor和多個(gè)輔助constructor埂软,而每個(gè)輔助的第一行都必須是調(diào)用其他輔助constructor或者主constructor锈遥,因此子類的輔助constructor是一定不可能直接調(diào)用父類的constructor的
// 只能在子類的主constructor中調(diào)用父類的constructor => Person(name, age)
// 【注意】如果是父類中接收的參數(shù)纫事,比如name和age,子類中接收時(shí)所灸,就不要用任何val或者var來修飾丽惶,否則會(huì)任務(wù)是子類要覆蓋父類的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)
  }
}

內(nèi)部類

import scala.collection.mutable.ArrayBuffer
class Class{
  class Student(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  // [error]type mismatch s2是c2外部類的Student實(shí)例,c1無法獲得

object

// object相當(dāng)于class的單個(gè)實(shí)例爬立,通常放一些靜態(tài)的field或者method钾唬,第一次調(diào)用object的方法時(shí),就會(huì)執(zhí)行object的constructor侠驯,也就是object內(nèi)部不在method中的代碼抡秆,但是object不能定義接受參數(shù)的constructor
// 注意,object的constructor只會(huì)在第一次被調(diào)用時(shí)執(zhí)行吟策,以后不會(huì)再次執(zhí)行
// object通常用于作為單例模式的實(shí)現(xiàn)儒士,或者放class的靜態(tài)成員,比如工具方法
object Person{
    private eyeNum = 2
    println("this Person object")
    def getEyeNum = eyeNum
}
/*******讓object繼承抽象類*******/
abstract class Hello(var msg:String){
  def sayHello(name: String): Unit
}
object HelloImpl extends Hello("hello"){
  override def sayHello(name: String) = {
    println(msg+", "+name)
  }
}
HelloImpl.sayHello("leo")
/******用object實(shí)現(xiàn)枚舉功能******/
object Season extends Enumeration{
  val SPRING, SUMMER, AUTUMN, WINTER = Value // 調(diào)用Value方法來初始化枚舉值
}
Season.WINTER // Seaon.Value = WINTER
// 還可以用過Value傳入枚舉值的id和name檩坚,
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.SPRING.id
Season.SPRING.toString
// 可以反過來通過id和name找到枚舉值
Season(0)
Season.withName("Spring")
// 增強(qiáng)for循環(huán)之間打印所有枚舉值
for (ele <- Season.values) prinln(ele)

apply方法

// object中非常重要的一個(gè)特殊方法就是apply方法着撩,通常在object中實(shí)現(xiàn)apply方法,并在其中實(shí)現(xiàn)構(gòu)造object對(duì)象的功能
// 而創(chuàng)建伴生類的對(duì)象時(shí)匾委,通常不會(huì)使用new class的方式睹酌,而是使用Class()的方式,隱式地調(diào)用伴生對(duì)象的apply方法剩檀,這樣會(huì)讓對(duì)象創(chuàng)建更加簡潔憋沿,如 val a = Array(1,2,3,4,5)
class Person(val name:String)
object Person{
  def apply(name:String) = new Person(name)
}
val p = Person("leo")

main方法

// scala的main方法必須在object中實(shí)現(xiàn)
// App Trait的工作原理為:App Trait繼承自DelayedInit Trait,scalac命令進(jìn)行編譯時(shí)沪猴,會(huì)把繼承App Trait的object的constructor代碼都放到DelayedInit Trait的delayedInit方法中執(zhí)行
object HelloWorld extends App{
    if (args.length > 0) println("Hello, "+args(0))
    else println("Hello World!")
}
// shell
scalac HelloWorld.scala
scala HelloWorld leo
scala -Dscala.time HelloWorld  // 同時(shí)顯示程序運(yùn)行時(shí)間

繼承

class Person{
  private var name = "leo"
  def getName = name
}
class Student extends Person{
  private var score = "A"
  def getScore = score
}
/**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  // 注意用super
}
/**override field**/
class Person{
  val name: String = "Person"
  def age: Int = 0
}
class Student extends Person{
  override val name: String = "leo"
  override val age: Int = 30  // 覆蓋的是def方法
}

isInstanceOf和asInstanceOf

// 如果我們創(chuàng)建了子類的對(duì)象辐啄,又將其賦予了父類類型的變量。則在后續(xù)程序中运嗜,我們又需要將父類類型的變量轉(zhuǎn)換為子類類型的變量壶辜,應(yīng)該如何做?
// 首先担租,需要使用isInstanceOf判斷對(duì)象是否是指定類的對(duì)象砸民,若是,則可以用asInstanceOf將對(duì)象轉(zhuǎn)換為指定類型
// 注意奋救,如果對(duì)象是null岭参,則isInstanceOf一定返回false,asInstanceOf一定返回null
// 注意尝艘,如果沒有用isInstanceOf先判斷對(duì)象是否為指定類的實(shí)例演侯,就直接用asInstanceOf轉(zhuǎn)換,則可能會(huì)拋出異常
class Person
class Student extends Person
val p: Person = new Student
val s: Student = null
if (p.isInstanceOf[Student])  s = p.asInstanceOf[Student]

getClass和classOf

// 對(duì)象.getClass可以精確獲取對(duì)象的類背亥,classOf[類]可以精確獲取類秒际,然后使用==操作符即可判斷
class Person
class Student extends Person
val p: Person = new Student
p.isInstanceOf[Person]   //true
p.isInstanceOf[Student]  //true
p.getClass == classOf[Person]  // false
p.getClass == classOf[Student] // true

使用模式匹配進(jìn)行類型判斷

// 類似isInstanceOf悬赏,不是精確判斷
p match{
  case per: Person => println("person")
  case _ => println("unknown type")
}

匿名子類

// 匿名內(nèi)部類,就是說可以定義一個(gè)類的沒有名稱的子類娄徊,并直接創(chuàng)建其對(duì)象闽颇,然后將對(duì)象的引用賦予一個(gè)變量。之后甚至可以將該匿名子類的對(duì)象傳遞給其他函數(shù)寄锐。
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)
}
greeting(p) // hi i'm leo

抽象類

abstract class Person(val name: String){
  def sayHello: Unit
}
class Student(name: String) extends Person{
  def sayHello: Unit = println("hello "+name)  // 不用override
}
// 抽象field兵多,在父類中定義了field,但沒有給出初始值锐峭,則此field為抽象field
abstract class Person{
  val name: String
}
class Student extends Person{
  val name: String = "leo"
}

Trait

將Trait作為接口使用

// 類繼承trait后中鼠,必須實(shí)現(xiàn)其中的抽象方法可婶,實(shí)現(xiàn)時(shí)不需要使用override
// scala不支持對(duì)類進(jìn)行多重繼承沿癞,但可以用with繼承多個(gè)trait
trait SayHello{
  def sayHello(name: String)
}
trait MakeFriends{
  def makeFriends(p: Person)
}
class Person(val name: String) extends SayHello with MakeFriends{
  def sayHello(name: String) = println("hello "+ name)
  def makeFriends(p: Person) = println("hi " + p.name)
}
val p1 = new Person("leo")
val p2 = new Person("anna")
p1.makeFriends(p2)

在Trait中定義具體方法/字段

// Trait中可以包含一些很多類都通用的方法,比如打印日志等矛渴,spark中就使用了Logger trait
trait Logger{
  def log(msg: String) = println(msg)
}
class Person(val name: String) extends Logger{
  def makeFriends(p: Person){
    println("hi i'm "+ name +", nice to meet you "+ p.name)
    log("makeFriends method is invoked with parameter Person[name="+p.name+"]")
  }
}
// 定義字段
trait Person{
  val eyeNum: Int = 2
}
class Student(val name: String) extends Person{
  def sayHello = println("hi i'm "+ name + ", i have " + eyeNum + " eyes.")
}

在Trait中定義抽象字段

trait SayHello{
  val msg: String
  def sayHello(name: String) = println(msg+", "+name)
}
class Person(val name: String) extends SayHello{
  val msg: String = "hello"
  def makeFriends(p: Person){
    sayHello(p.name)
    println("I'm "+name+"!")
  }
}

為實(shí)例混入trait

// 有時(shí)我們可以在創(chuàng)建類的對(duì)象時(shí)椎扬,指定該對(duì)象混入某個(gè)trait娄涩,這樣钻蹬,就只有這個(gè)對(duì)象混入該trait的方法障贸,而類的其他對(duì)象則沒有
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  // 動(dòng)態(tài)地混入trait

trait調(diào)用鏈

// scala中支持讓類繼承多個(gè)trait后复隆,依次調(diào)用多個(gè)trait中的同一個(gè)方法捌锭,只要讓多個(gè)trait的同一個(gè)方法中珊随,在最后都執(zhí)行super即可
// 類中調(diào)用多個(gè)trait中都有的這個(gè)方法時(shí)暑认,首先會(huì)從最右邊的trait方法開始執(zhí)行柏肪,然后依次往左執(zhí)行达皿,形成一個(gè)調(diào)用鏈條
// 這種特性非常強(qiáng)大天吓,其實(shí)就相當(dāng)于設(shè)計(jì)模式中的責(zé)任鏈模式的一種具體實(shí)現(xiàn)依賴
trait Handler{
  def handle(data: String){
  }
}
trait DataValidHandler extends Handler{
  override def handle(data: String){  // 覆蓋Handler的handle方法
    println("check data: "+data)  // 自己的邏輯
    super.handle(data)  // 調(diào)用下一個(gè)trait
  }
}
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)}
}

在trait中覆蓋抽象方法

// 覆蓋時(shí),如果使用了super.方法的代碼峦椰,則無法通過編譯龄寞,因?yàn)閟uper.方法就會(huì)去掉父trait的抽象方法,此時(shí)子trait的該方法還是會(huì)被認(rèn)為是抽象的
// 如果此時(shí)要通過編譯汤功,就得給子trait的方法加上abstract override修飾
trait Logger{
  def log(msg: String)
}
trait MyLogger extends Logger{
  abstract override def log(msg: String) {super.log(msg)}
}

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

// 在trait中可以混合使用trait的具體方法和抽象方法
// 可以讓具體方法依賴于抽象方法物邑,而抽象方法則放到繼承trait的類中去實(shí)現(xiàn)
// 這種trait其實(shí)就是設(shè)計(jì)模式中的模板設(shè)計(jì)模式的體現(xiàn)
trait Valid{
  def getName: String  // 抽象方法
  def valid: Boolean = {
    getName == "leo"
  }
}
class Person(val name: String) extends Valid{
  println(valid)
  def getName = name
}

trait的構(gòu)造機(jī)制

// 不包含在任何方法中的代碼就是trait的構(gòu)造代碼
// 而繼承了trait的類的構(gòu)造機(jī)制如下: 1、父類的構(gòu)造函數(shù)先執(zhí)行滔金;2色解、trait的構(gòu)造代碼執(zhí)行,多個(gè)trait從左到右依次執(zhí)行餐茵;3冒签、構(gòu)造trait時(shí)先構(gòu)造父trait,如果多個(gè)trait繼承同一個(gè)父trait钟病,則父trait只會(huì)構(gòu)造一次萧恕;4刚梭、所有trait構(gòu)造完畢之后,子類的構(gòu)造函數(shù)執(zhí)行
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()
/**
Person's constructor!
Logger's constructor!
MyLogger's constructor!
TimeLogger's constructor!
Student's constructor!
*/

trait field的初始化

// scala中票唆,trait是沒有接收參數(shù)的構(gòu)造函數(shù)的朴读,這是trait與class的唯一區(qū)別,但是如果需求是要trait能夠?qū)ield進(jìn)行初始化走趋,就只能用提前定義
trait SayHello{
  val msg: String
  println(msg.toString)
}
class Person extends SayHello{
  val msg: String = "init"
}
val p = new Person // error: java.lang.NullPointException 
// 過程: p先構(gòu)造父類(沒有父類)衅金,再構(gòu)造trait,此時(shí)由于msg沒初始化msg.toString肯定是空指針
  
/***提前定義***/
class Person
val p = new{
  val msg: String = "null"
} with Person with SayHello    // 動(dòng)態(tài)混入

class Person extends {
  val msg: String = "init"    // 因?yàn)橐葮?gòu)造父類
} with SayHello()
 
/***lazy value***/  
trait SayHello{
  lazy val msg: String = null   
  println(msg.toString)
}
class Person extends SayHello{
  override lazy val msg: String = "init"
}

trait繼承class

class MyUtil{
  def printMsg(msg: String) = println(msg)
}
trait Logger extends MyUtil {
  def log(msg: String) = printMsg("log: "+msg)
}
class Person(val name: String) extends Logger{
  def sayHello{
    log("hi i'm" + name)
    printMsg("hi i'm" + name)
  }
}

1.24 函數(shù)式編程

將函數(shù)賦值給變量

// scala中可以將函數(shù)作為值賦值為變量
// scala語法規(guī)定簿煌,將函數(shù)作為值賦值為變量時(shí)氮唯,必須在函數(shù)后加上空格和下劃線

def sayHello(name: String) = println("hello! "+name)
val sayHelloFunc = sayHello _
sayHelloFunc("leo")

匿名函數(shù)

// 匿名函數(shù):函數(shù)不需要命名
// 可以直接定義函數(shù)之后,將函數(shù)賦值給某個(gè)變量姨伟;也可以將直接定義的匿名函數(shù)傳入其他函數(shù)之中
// (參數(shù)名: 參數(shù)類型) => 函數(shù)體
val sayHelloFunc = (name: String) => println("hello, "+name)
sayHelloFunc("leo")

高階函數(shù)

// scala中可以直接將某個(gè)函數(shù)傳入其他函數(shù)惩琉,作為參數(shù)。是Java不具備的夺荒。
// 接收其他函數(shù)作為參數(shù)的函數(shù)瞒渠,稱作高階函數(shù)
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)
// 函數(shù)作為返回值 
def getGreetingFunc(msg: String) = (name: String) => println(msg+", "+name)
val greetingFunc = getGreetingFunc("hello")
greetingFunc("leo")  // hello, leo

高階函數(shù)的類型推斷

// 高階函數(shù)可以自動(dòng)推斷出參數(shù)類型,而不需要寫明類型技扼;而且對(duì)于只有一個(gè)參數(shù)的函數(shù)伍玖,還可以省去其小括號(hào);如果僅有一個(gè)參數(shù)在右側(cè)的函數(shù)體內(nèi)只使用一次剿吻,還可以將接收參數(shù)省略窍箍,并且將參數(shù)用_來替代
def greeting(func: (String) => Unit, name: String) {func(name)}
greeting((name) => println("hello "+name), "leo") // name的String類型可以不用寫了
greeting(name => println("hello "+name), "leo") // name的()可以不用寫了
greeting(_ => println("hello "+_), "leo") 

def triple(func: (Int)=>Int) = {func(3)}
triple(5+_)  // 8

scala的常用高階函數(shù)

// map
Array(1, 2, 3).map(2 * _)
// foreach: 對(duì)傳入的每個(gè)元素都進(jìn)行處理,但是沒有返回值
(1 to 9).map("*" * _).foreach(println _)
// filter: 對(duì)傳入的每個(gè)元素都進(jìn)行條件判斷
(1 to 20).filter(_ % 2 == 0)
// reduceLeft: 從左側(cè)開始丽旅,進(jìn)行reduce操作
(1 to 9).reduceLeft(_ * _)
//sortWith: 對(duì)元素進(jìn)行兩兩相比椰棘,進(jìn)行排序
Array(3,2,5,4,10,1).sortWith(_ < _) // Array(1,2,3,4,5,10)

閉包

// 函數(shù)在變量不處于其有效作用域時(shí),還能夠?qū)ψ兞窟M(jìn)行訪問魔招,即為閉包
def getGreetingFunc(msg: String) = (name: String) => println(msg+", "+name)
var greetingFuncHello = getGreetingFunc("hello")
val greetingFuncHi = getGreetingFunc("hi")
// msg只是一個(gè)局部變量晰搀,確診getGreetingFunc執(zhí)行完之后,還可以繼續(xù)存在創(chuàng)建的函數(shù)之中办斑,greetingFuncHello("leo")調(diào)用時(shí)外恕,值為"hello"的msg仍保留在了返回的函數(shù)體的內(nèi)部,可以反復(fù)的使用
// 這種變量超出了其作用域乡翅,還可以使用的情況鳞疲,即為閉包

// scala通過給每個(gè)函數(shù)創(chuàng)建對(duì)象來實(shí)現(xiàn)閉包,實(shí)際上對(duì)于getGreetingFunc函數(shù)創(chuàng)建的函數(shù)蠕蚜,msg是作為函數(shù)對(duì)象的變量存在的尚洽,因此每個(gè)函數(shù)才可以擁有不同的msg

SAM轉(zhuǎn)換

// SAM-single abstract method
// 在Java中,不支持直接將函數(shù)傳入一個(gè)方法作為參數(shù)靶累,通常來說腺毫,唯一的辦法就是定義一個(gè)實(shí)現(xiàn)了某個(gè)接口的類的實(shí)例對(duì)象癣疟,該對(duì)象只有一個(gè)方法;而這些接口都只有單個(gè)的抽象方法潮酒,也就是SAM

// 在調(diào)用Java方法時(shí)睛挚,使用的功能,SAM轉(zhuǎn)換急黎,即將SAM轉(zhuǎn)換為Scala函數(shù)
// 要使用SAM轉(zhuǎn)換扎狱,需要使用Scala提供的特性,隱式轉(zhuǎn)換
import javax.swing._
import java.awt.event._
val button = new JButton("Click")
button.addActionListener(new ActionListener{
  override def actionPerformed(event: ActionEvent){
    println("Click Me!!")
  }
}) 
// 隱式轉(zhuǎn)換
// 接收參數(shù)為ActionEvent勃教,返回值為Unit的匿名函數(shù)
implicit def convertActionListener(actionProcessFunc:(ActionEvent)=>Unit) = new ActionListener{
  override def actionPerformed(event:ActionEvent){
    actionProcessFunc(event)
  }
}
button.addActionListener((event: ActionEvent) => println("Click Me!!"))

Currying 柯里函數(shù)

// 把原來接收兩個(gè)參數(shù)的一個(gè)函數(shù)淤击,轉(zhuǎn)換為兩個(gè)函數(shù),第一個(gè)函數(shù)接收原先的第一個(gè)參數(shù)故源,然后返回接收原先第二個(gè)參數(shù)的第二個(gè)函數(shù)污抬。
def sum(a: Int, b: Int)  = a+b
sum(1,1)
def sum2(a: Int) = (b: Int) => a+b  // 返回值為匿名函數(shù)
sum2(1)(1)
def sum3(a: Int)(b: Int) = a+b

return

// Scala中,不需要使用return來返回函數(shù)的值心软,使用return的匿名函數(shù)壕吹,是必須給出返回類型的著蛙,否則無法通過編譯
def greeting(name: String) = {
  def sayHello(name:String): String = {
    return "Hello, " + name
  }
  sayHello(name)
}

1.26 集合

List

immutable

val list = List(1,2,3,4)
list.head // Int = 1
list.tail // List(2,3,4)
// List的 :: 操作方法删铃,可以用于將head和tail合并成一個(gè)List, 0::List = List(0,1,2,3,4)

// 案例:用遞歸函數(shù)來給List中的每個(gè)元素都加上指定前綴并打印
def decorator(list: List[Int], prefix: String){
  if (list != Nil){
    println(prefix + list.head)
    decorator(list.tail, prefix)  // 當(dāng)list = (4)時(shí)踏堡,list.tail = Nil
  }
}

LinkedList

val ll = scala.collection.mutable.LinkedList(1,2,3,4,5)
ll.elem // 頭元素猎唁,類似head
ll.next // 類似tail
// 案例:使用while循環(huán)將LinkedList的每個(gè)元素*2
var currentList = ll  // 【注意】對(duì)currentList的元素操作,ll的元素也隨之改變
while (currentList != Nil){
  currentList.elem = currentList.elem*2
  currentList = currenList.next
}

// 使用while循環(huán)將LinkedList每隔一個(gè)元素*2
var ll = scala.collection.mutable.LinkedList(1,2,3,4,5,6,7,8,9,10)
var currentList = ll 
var fisrt = true
while(currentList != Nil && currentList.next != Nil){ //注意兩重判斷
  if(f) {currentList.elem *= 2;first = false}
  currentList = currentList.next.next
  if (currentList != Nil) currentList.elem *= 2
}
ll

Set

val s = Set(1,2,3)
s + 1 // Set(1,2,3)
s + 4 // Set(1,2,3,4)
// Set不保證插入順序
s = new scala.collection.mutable.HashSet[Int](); s+=1;s+=2;s+=3
// LinkedHashSet可以保證插入順序
val s = scala.collection.mutable.LinkedHashSet[Int]();s+=1;s+=2;s+=3

集合的函數(shù)式編程

// Java里面是沒有函數(shù)式編程的顷蟆,所以也沒有高階函數(shù)诫隅,也無法直接將函數(shù)傳入一個(gè)方法,或者讓一個(gè)方法返回一個(gè)函數(shù)
List("a","b","c").map("xixi"+_)
List("hello world", "you me").flatMap(_.split(" ")) // List(hello, world, you, me)
List("I","have","an","apple").foreach(println(_))
List("leo", "jen", "jack").zip(List(100,70,80)) // List((leo,100), (jen,70), (jack,80))
  
 /****單詞計(jì)數(shù)****/
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
val lines = List(lines01, lines02)
lines.flatMap(_.split(" ")).map((_, 1)).map(_._2).reduceLeft(_+_)

1.28 模式匹配

// Scala的模式匹配除了值還可以對(duì)類型進(jìn)行匹配帐偎,對(duì)Array和List的元素情況進(jìn)行匹配逐纬,對(duì)case class進(jìn)行匹配
// match case里,只要一個(gè)分支滿足了就不會(huì)進(jìn)行處理了削樊,所以不需要break
def judgeGrade(name: String, grade: String){
  grade match{
    case "A" => println("A")
    case "B" => println("B")
    case _ if name == "leo" => println(name + "you are a good boy")  // if守衛(wèi)
    case _ => println("o")
  }
}

//在模式匹配中進(jìn)行變量賦值(對(duì)于_的情況)
def judgeGrade(grade: String){
  grade match{
    case "A" => println("A")
    case "B" => println("B")
    case badGrade => println(badGrade)
  }
}

對(duì)類型進(jìn)行模式匹配

import java.io._
def processException(e: Exception){
  e match{
    case e1: IllegalArgumentException => println(e1)
    case e2: FileNotFoundException => println(e2)
    case e3: IOException => println(e3)
    case _: Exception => println("idk your exception")
  }
}
processException(new IOException("This is an IOException!"))

對(duì)Array和List進(jìn)行模式匹配

// 對(duì)Array進(jìn)行模式匹配豁生,分別可以匹配帶有指定元素的數(shù)組,帶有指定個(gè)數(shù)元素的數(shù)組和以某元素開頭的數(shù)組
// 對(duì)List進(jìn)行模式匹配漫贞,與Array類似甸箱,但是需要使用List特有的::操作符

//案例:對(duì)朋友打招呼
def greeting(arr: Array[String]){
  arr match{
    case Array("leo") => println("hi leo")
    case Array(girl1, girl2, girl3) => println("hi, girls")  // Array("leo",g1,g2)會(huì)被匹配
    case Array("leo",_*) => println("hi leo, please introduce your friends")
    case _ => println("hey who are you")
  }
}

def greeting(list: List[String]){
  list match{
    case "leo"::Nil => println("hi leo")
    case girl1::girl2::girl3::Nil => println("hi girls")
    case "leo"::tail => println("hi leo who are they")
    case _ => println("who are you")
  }
}

case class

// case class樣例類,類似JavaBean迅脐,只定義field芍殖,且由scala編譯器自動(dòng)提供getter和setter方法,但是沒有method谴蔑,case class的主構(gòu)造函數(shù)接收的參數(shù)通常不需要使用var或val修飾豌骏,scala自動(dòng)就會(huì)使用val修飾
// scala自動(dòng)為case class定義了伴生對(duì)象龟梦,也就是object,并且定義了apply()方法窃躲,該方法接收主構(gòu)造函數(shù)中相同的參數(shù)变秦,并返回case class對(duì)象
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+" "+subject)
    case Student(name, classroom) => println("student "+name+" "+classroom)
    case _ => println("illegal")
  }
}
val leo: Person = Student("leo", "class1")
val tom: Person = Teacher("leo", "Math")
case class Worker(name: String) extends Person
val jack: Person = Worker("jack")

Option

// Option有兩種值,一種是Some表示有值框舔, 一種是None蹦玫,表示沒有值
// Option通常會(huì)用于模式匹配中,用于判斷某個(gè)變量是有值還是沒有值刘绣,這比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 no your grade")
  }
}

1.30 類型參數(shù)

class Student[T](val localID: T){
  def getSchoolID(hukouID: T) = "S-"+hukouID+"-"+localID
}
val leo = new Student[Int](111)
leo.getSchoolID("2") // 不行 required:Int

泛型函數(shù)

// 泛型函數(shù)樱溉,與泛型類類似,可以給某個(gè)函數(shù)在聲明時(shí)指定泛型類型纬凤,然后在函數(shù)體內(nèi)福贞,多個(gè)變量或者返回值直接,就可以使用泛型類型進(jìn)行聲明停士,從而對(duì)某個(gè)特殊的變量挖帘,或者多個(gè)變量,進(jìn)行強(qiáng)制性的類型限制恋技。
// 與泛型類一樣拇舀,你可以通過給使用了泛型類型的變量傳遞值來讓Scala自動(dòng)推斷泛型的實(shí)際類型,也可以在調(diào)用函數(shù)時(shí)蜻底,手動(dòng)指定泛型類型骄崩。

/**案例: 卡片售賣機(jī),可以指定卡片的內(nèi)容薄辅,內(nèi)容可以是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")

上邊界Bounds

// 在指定泛型類型的時(shí)候要拂,有時(shí),我們需要對(duì)泛型類型的范圍進(jìn)行界定站楚,而不是可以是任意的類型脱惰,比如,我們可能要求某個(gè)泛型類型窿春,它就必須是某個(gè)類的子類拉一,這樣在程序中就可以放心地調(diào)用泛型類型繼承的父類的方法,程序才能正常的使用和運(yùn)行谁尸。此時(shí)就可以使用上下邊界Bounds的特性舅踪。
// Scala的上下邊界特性允許泛型類型必須是某個(gè)類的子類,或者必須是某個(gè)類的父類

/***案例: 在派對(duì)上交朋友***/
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){
  def play = p1.makeFriends(p2)
}
class Worker(val name: String)
val leo = new Student("leo")
val tom = new Worker("tom")
val party = new Party(leo, tom) // error
val jack = new Student("jack")
val party = new Party(leo, jack)
party.play()

下邊界Bounds

// 下邊界:即指定泛型類型必須是某個(gè)類的父類
class Father(val name: String)
class Child(name: String) extends Father(name)
def getIDCard[R >: Child](person: R){
  if(person.getClass == classOf[Child]) println("child")
  else if (person.getClass == classOf[Father]) println("father")
  else println("your are not allowed to getIDCard")
}

View Bounds

// 上下邊界Bounds良蛮,雖然可以讓一種泛型類型抽碌,支持有父子關(guān)系的多種類型。但是,在某個(gè)類與上下邊界Bounds指定的父子類型范圍內(nèi)的類都沒有任何關(guān)系時(shí)货徙,則默認(rèn)是不能接收的左权。
// 然而,View Bounds作為一種上下邊界Bounds的加強(qiáng)版痴颊,支持可以對(duì)類型進(jìn)行隱式轉(zhuǎn)換赏迟,將指定的類型進(jìn)行隱式轉(zhuǎn)換后,再判斷是否在邊界指定的類型范圍內(nèi)

/***跟小狗交朋友***/
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("won won i'm "+name)
}
implicit def dog2person(obj: Object): Person = if(obj.isInstanceOf[Dog]){
  val dog = obj.asInstanceOf[Dog]
  new Person(dog.name)
}else Nil
class Party[T <% Person](p1: T, p2: T){}  // <%
val leo = new Student("leo")
val doggy = new Dog("doggy")
val party = new Party(leo, doggy)

Context Bounds

// Context Bounds是一種特殊的Bounds蠢棱,它會(huì)根據(jù)泛型類型的聲明锌杀,比如“T: 類型”要求必須存在一個(gè)類型為“類型[T]”的隱式值。

/***案例:使用Scala內(nèi)置的比較器比較大小***/
class Calculator[T: Ordering](val num1: T, val num2: T){
  def max(implicit order: Ordering[T]) = if(order.compare(num1, num2) > 0) num1 else num2
}
val cal = new Calculator(1,5) // cal: Calculator[Int] = Calculator@2a76840c
cal.max  // 5

Manifest Context Bounds

// 在Scala中泻仙,如果要實(shí)例化一個(gè)泛型數(shù)組糕再,就必須使用Manifest Context Bounds。也就是說玉转,如果數(shù)組元素類型為T的話突想,需要為類或者函數(shù)定義[T:Manifest]泛型類型,這樣才能實(shí)例化Array[T]這種泛型數(shù)組究抓。

/***案例:打包飯菜(一種食品打成一包)***/
class Meat(val name: String)
class Vegetable(val name: String)
def packageFood(foods: T*) = {     // foods: T* 變長參數(shù)
  val foodPackage = new Array[T](foods.length)
  for(i <- 0 until foods.length) foodPackage(i) = foods(i)
  foodPackage
}
val yuxiangrousi = new Meat("yuxiangrousi")
val yangpai = new Meat("yangpai")
val bocai = new Vegetable("bocai")
packageFood(yuxiangrousi, yangpai) // Array[Object] = Array(Meat@2ca65ce4, Meat@7957dc72, Vegetable@6058e535)
packageFood(yuxiangrousi, yangpai, bocai) // Array[Meat] = Array(Meat@2ca65ce4, Meat@7957dc72)

協(xié)變和逆變

// SCala的協(xié)變和逆變是非常有特色的猾担,完全解決了Java中的泛型的一大缺憾
// 舉例來說,Java中刺下,如果有Professional是Master的子類绑嘹,那么Card[Professional]不是Card[Master]的子類。而Scala中怠李,只要靈活使用協(xié)變和逆變圾叼,就可以解決Java泛型的問題蛤克。

/***案例:進(jìn)入會(huì)場***/
class Master
class Professional extends Master
  // 大師以及大師級(jí)別以下的名片都可以進(jìn)入會(huì)場
class Card[+T](val name: String) // +T 協(xié)變
def enterMeet(card: Card[Master]){
  println("welcomt to this meeting "+ card.name)
}
val leo = new Card[Master]("leo")
val jack = new Card[Professional]("jack")
jack.isInstanceOf[Card[Master]]  // true Card[Professional]是Card[Master]的子類
enterMeet(jack)  // 可以進(jìn)入
  // 專家及專家以上級(jí)別的名片都可以進(jìn)入會(huì)場
class Card[-T](val name: String)  // -T 逆變 含義是把Master強(qiáng)制轉(zhuǎn)換為Professional的子類
val leo = new Card[Professional]("leo")
val jack = new Card[Master]("jack")
jack.isInstanceOf[Card[Professional]]  // true
def enterMeet(card: Card[Professional]){
  println("welcomt to this meeting "+ card.name)
}
enterMeet(jack)

Existential Type

// 在Scala里捺癞,有一種特殊的類型參數(shù),就是Existential Type构挤,存在性類型
Array[T] forSome {type T}
Array[_]

1.32 隱式轉(zhuǎn)換和隱式參數(shù)

Scala的隱式轉(zhuǎn)換髓介,最核心的就是定義隱式轉(zhuǎn)換參數(shù),即implicit conversion function筋现。定義的隱式轉(zhuǎn)換函數(shù)唐础,只要在編寫的程序內(nèi)引入,就會(huì)被Scala自動(dòng)使用矾飞。Scala會(huì)根據(jù)隱式轉(zhuǎn)換函數(shù)的簽名一膨,在程序中使用到隱式轉(zhuǎn)換接收的參數(shù)類型定義的對(duì)象時(shí),會(huì)自動(dòng)將其傳入隱式轉(zhuǎn)換函數(shù)洒沦,轉(zhuǎn)換為另外一種類型的對(duì)象并返回豹绪。這就是“隱式轉(zhuǎn)換”。

隱式轉(zhuǎn)換

// 要實(shí)現(xiàn)隱式轉(zhuǎn)換申眼,只要程序可見的范圍內(nèi)定義隱式轉(zhuǎn)換函數(shù)即可瞒津,Scala會(huì)自動(dòng)使用隱式轉(zhuǎn)換函數(shù)蝉衣。隱式轉(zhuǎn)換函數(shù)與普通函數(shù)唯一的語法區(qū)別就是,要以implicit開頭巷蚪,而且最好要定義函數(shù)返回類型病毡。
/***案例:特殊售票窗口(只接受特殊人群,比如學(xué)生屁柏、老人等)***/
class SpecialPerson(val name: String)
class Student(val name: String)
class Oldman(val name: String)
implicit object2SpecialPerson(obj: Object): SpecialPerson = {
  if(obj.getClass == classOf[Student]) {val stu = obj.asInstanceOf[Student]; new SpecialPerson(stu.name)}
  else if(obj.getClass == classOf[Oldman]) {val old = obj.asInstanceOf[Oldman]; new SpecialPerson(old.name)}
  else Nil
}
var ticketNum = 0
def buySpecialTicket(p: SpecialPerson) = {
  ticketNum += 1
  "T-"+ticketNum
}
val s = new Student("stu")
buySpecialTicket(s)

使用隱式轉(zhuǎn)換加強(qiáng)現(xiàn)有類型

// 隱式轉(zhuǎn)換非常強(qiáng)大的一個(gè)功能啦膜,就是可以加強(qiáng)現(xiàn)有類型的功能。也就是說淌喻,可以為某個(gè)類定義一個(gè)加強(qiáng)版的類功戚,并定義互相之間的隱式轉(zhuǎn)換,從而讓源類在使用加強(qiáng)版的方法時(shí)似嗤,有Scala自動(dòng)進(jìn)行隱式轉(zhuǎn)換為加強(qiáng)類啸臀,然后再調(diào)用該方法。
/***案例:超人變身***/
class Man(val name: String)
class Superman(val name: String){
  def emitLaser = println("emit laser")
}
implicit def man2superman(man: Man): Superman = new Superman(man.name)
val leo = new Man("leo")
leo.emitLaser

導(dǎo)入隱式轉(zhuǎn)換

Scala默認(rèn)會(huì)使用兩種隱式轉(zhuǎn)換烁落,一種是源類型乘粒,或者目標(biāo)類型的伴生對(duì)象內(nèi)的隱式轉(zhuǎn)換函數(shù);一種是當(dāng)前程序作用域內(nèi)的可以用唯一標(biāo)識(shí)符表示的隱式轉(zhuǎn)換函數(shù)伤塌。

如果隱式轉(zhuǎn)換函數(shù)不在上述兩種情況下的話灯萍,那么就必須手動(dòng)使用import語法引入某個(gè)包下的隱式轉(zhuǎn)換函數(shù),比如import test._ 通常建議每聪,僅僅在需要進(jìn)行隱式轉(zhuǎn)換的地方旦棉,比如某個(gè)函數(shù)或者方法內(nèi),用import導(dǎo)入隱式轉(zhuǎn)換函數(shù)药薯,避免不需要的隱式轉(zhuǎn)換绑洛。

隱式轉(zhuǎn)換的發(fā)生時(shí)機(jī)

  1. 調(diào)用某個(gè)函數(shù),但是給函數(shù)傳入的參數(shù)的類型童本,與函數(shù)定義的接收參數(shù)類型不匹配
  2. 使用某個(gè)類型的對(duì)象真屯,調(diào)用某個(gè)方法,而這個(gè)方法并不存在于該類型時(shí)
  3. 使用某個(gè)類型的對(duì)象調(diào)用某個(gè)方法穷娱,雖然該類型有這個(gè)方法绑蔫,但是給方法傳入的參數(shù)類型,與方法定義的接收參數(shù)的類型不匹配
/***3. 案例:特殊售票窗口加強(qiáng)版***/
class TicketHouse{
  var ticketNum = 0
  def buySpecialTicket(p: SpecialPerson) = {
    ticketNum += 1
    "T-"+ticketNum
  }
}
val ticketHouse = new TicketHouse
val leo = new Student("leo")
ticketHouse.buySpecialTicket(leo)

隱式參數(shù)

// 所謂的隱式參數(shù)泵额,指的是在函數(shù)或者方法中配深,定義一個(gè)用implicit修飾的參數(shù),此時(shí)Scala會(huì)嘗試找到一個(gè)指定類型的嫁盲,用implicit修飾的對(duì)象篓叶,即隱式值,并注入?yún)?shù)。
// Scala會(huì)在兩個(gè)范圍內(nèi)查找:一種是當(dāng)前作用域內(nèi)可見的val或var定義的隱式變量澜共;一種是隱式參數(shù)類型的伴生對(duì)象內(nèi)的隱式值
/***案例:考試簽到***/
class SignPen{
  def write(cotent: String) = println(content)
}
implicit val signPen = new SignPen   // 簽到只需要一只筆向叉!
def signForExam(name: String)(implicit signPen: SignPen){
  signPen.write(name+" come to exam in time.")
}

1.33 Actor入門

Actor類似Java的多線程編程,但是又有所不同嗦董。Scala的Actor盡可能地避免鎖和共享狀態(tài)母谎,從而避免多線程并發(fā)時(shí)出現(xiàn)資源爭用的情況,進(jìn)而提升多線程編程的性能京革。此外奇唤,Scala Actor的這種模型還可以避免死鎖等一系列傳統(tǒng)多線程編程的問題。

Spark中使用的分布式多線程框架匹摇,是Akka咬扇。Akka也實(shí)現(xiàn)了類似Scala Actor的模型,其核心概念同樣也是Actor廊勃。

Actor的創(chuàng)建懈贺、啟動(dòng)和消息收發(fā)

// Scala提供了Actor trait來讓我們更方便地進(jìn)行actor多線程編程,就Actor trait就類似于Java中的Thread和Runnable一樣坡垫,是基礎(chǔ)的多線程基類和接口梭灿。我們只要重寫Actor trait的act方法,即可實(shí)現(xiàn)自己的線程執(zhí)行體冰悠,與Java中重寫run方法類似堡妒。此外,使用start方法啟動(dòng)Actor溉卓,使用!向Actor發(fā)送消息皮迟,Actor使用receive和模式匹配接收消息。

/***案例:Actor Hello World***/
class HelloActor extends Actor{
  def act(){
    while(true){
      receive{case name: String => println("hello "+name)}
    }
  }
}
val helloActor = new HelloActor
helloActor.start()
helloActor!"leo"   // hello leo

收發(fā)case class類型的消息

/***案例:用戶注冊登錄后臺(tái)接口***/
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: "+name+", "+password)
          case Register(username, password) => println("register: "+name+", "+password)
      }
    }
  }
}
val userManageActor = new UserManageActor
userManageActor.start()
userManageActor ! Register("leo", "123")

Actor之間互相收發(fā)消息

// 如果兩個(gè)Actor之間要互相收發(fā)消息桑寨,那么Scala建議一個(gè)Actor向另一個(gè)Actor發(fā)送消息時(shí)伏尼,同時(shí)帶上自己的引用;其他Actor收到自己的消息時(shí)西疤,直接通過發(fā)送消息的actor的引用烦粒,即可以給它回復(fù)消息。
/***案例:打電話***/
case class Message(content: String, sender: Actor)
class LeoActor extends Actor{
  def act(){
    while(true){
      receive{
        case Message(content, sender) => {
          println("leo: "+content)
          sender!"please call me later."  // 注意!不能有空格
        }
      }
    }
  }
}

class JackActor(val leoActor: Actor) extends Actor{
  def act(){
    leoActor!Message("hi i'm jack", this) // 傳jackActor自己的引用
    receive{
      case response: String => println("jack telephone: "+response)
    }
  }
}
val leoActor = new LeoActor
leoActor.start()
val jackActor = new JackActor(leoActor)
jackActor.start()

同步消息和Future

// 默認(rèn)情況下代赁,消息都是異步的;但是如果希望發(fā)送的消息是同步兽掰,即對(duì)方接收后芭碍,一定要給自己返回結(jié)果,那么可以使用!?的方式發(fā)送消息孽尽。
val reply = actor!?message
// 如果要異步發(fā)送一個(gè)消息窖壕,但是在后續(xù)要獲得消息的返回值,那么可以使用Future。即!!語法瞻讽。
val future = actor!!message
val reply = future()
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸳吸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子速勇,更是在濱河造成了極大的恐慌晌砾,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烦磁,死亡現(xiàn)場離奇詭異养匈,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)都伪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門呕乎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人陨晶,你說我怎么就攤上這事猬仁。” “怎么了先誉?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵逐虚,是天一觀的道長。 經(jīng)常有香客問我谆膳,道長叭爱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任漱病,我火速辦了婚禮买雾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杨帽。我一直安慰自己漓穿,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布注盈。 她就那樣靜靜地躺著晃危,像睡著了一般。 火紅的嫁衣襯著肌膚如雪老客。 梳的紋絲不亂的頭發(fā)上僚饭,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音胧砰,去河邊找鬼鳍鸵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛尉间,可吹牛的內(nèi)容都是我干的偿乖。 我是一名探鬼主播击罪,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贪薪!你這毒婦竟也來了媳禁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤画切,失蹤者是張志新(化名)和其女友劉穎竣稽,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體槽唾,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丧枪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了庞萍。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拧烦。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钝计,靈堂內(nèi)的尸體忽然破棺而出恋博,到底是詐尸還是另有隱情,我是刑警寧澤私恬,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布债沮,位于F島的核電站,受9級(jí)特大地震影響本鸣,放射性物質(zhì)發(fā)生泄漏疫衩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一荣德、第九天 我趴在偏房一處隱蔽的房頂上張望闷煤。 院中可真熱鬧,春花似錦涮瞻、人聲如沸鲤拿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽近顷。三九已至,卻和暖如春宁否,著一層夾襖步出監(jiān)牢的瞬間窒升,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工家淤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留异剥,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓絮重,卻偏偏與公主長得像冤寿,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子青伤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容