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ī)
- 調(diào)用某個(gè)函數(shù),但是給函數(shù)傳入的參數(shù)的類型童本,與函數(shù)定義的接收參數(shù)類型不匹配
- 使用某個(gè)類型的對(duì)象真屯,調(diào)用某個(gè)方法,而這個(gè)方法并不存在于該類型時(shí)
- 使用某個(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()