[TOC]
# Scala基礎語法
## 1、Scala基礎
### 1.1、聲明變量
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/6.
*/
object VariableDemo {
def main(args: Array[String]) {
//使用val定義的變量值是不可變的,相當于java里用final修飾的變量
val i = 1
//使用var定義的變量是可變得,在Scala中鼓勵使用val
var s = "hello"
//Scala編譯器會自動推斷變量的類型,必要的時候可以指定類型
//變量名在前,類型在后
val str: String = "itcast"
}
}
~~~
### 1.2、常用類型
> Scala和Java一樣,有7種數值類型Byte、Char、Short、Int、Long、Float和Double(無包裝類型)和一個Boolean類型
### 1.3、條件表達式
> Scala的的條件表達式比較簡潔,例如:
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/7.
*/
object ConditionDemo {
def main(args: Array[String]) {
val x = 1
//判斷x的值,將結果賦給y
val y = if (x > 0) 1 else -1
//打印y的值
println(y)
//支持混合類型表達式
val z = if (x > 1) 1 else "error"
//打印z的值
println(z)
//如果缺失else,相當于if (x > 2) 1 else ()
val m = if (x > 2) 1
println(m)
//在scala中每個表達式都有值,scala中有個Unit類,寫做(),相當于Java中的void
val n = if (x > 2) 1 else ()
println(n)
//if和else if
val k = if (x < 0) 0
else if (x >= 1) 1 else -1
println(k)
}
}
~~~
### 1.4、塊表達式
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/7.
*/
object BlockExpressionDemo {
def main(args: Array[String]) {
val x = 0
//在scala中{}中課包含一系列表達式,塊中最后一個表達式的值就是塊的值
//下面就是一個塊表達式
val result = {
if (x < 0){
-1
} else if(x >= 1) {
1
} else {
"error"
}
}
//result的值就是塊表達式的結果
println(result)
}
}
~~~
### 1.5、循環
> 在scala中有for循環和while循環,用for循環比較多
> for循環語法結構:for (i <- 表達式/數組/集合)
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/7.
*/
object ForDemo {
def main(args: Array[String]) {
//for(i <- 表達式),表達式1 to 10返回一個Range(區間)
//每次循環將區間中的一個值賦給i
for (i <- 1 to 10)
println(i)
//for(i <- 數組)
val arr = Array("a", "b", "c")
for (i <- arr)
println(i)
//高級for循環
//每個生成器都可以帶一個條件,注意:if前面沒有分號
for(i <- 1 to 3; j <- 1 to 3 if i != j)
print((10 * i + j) + " ")
println()
//for推導式:如果for循環的循環體以yield開始,則該循環會構建出一個集合
//每次迭代生成集合中的一個值
val v = for (i <- 1 to 10) yield i * 10
println(v)
}
}
~~~
### 1.6、調用方法和函數
> Scala中的+ - * / %等操作符的作用與Java一樣,位操作符 & | ^ >> <<也一樣。只是有
> 一點特別的:這些操作符實際上是方法。例如:
~~~
a + b
是如下方法調用的簡寫:
a.+(b)
a 方法 b可以寫成 a.方法(b)
~~~
### 1.7、定義方法和函數
#### 1.7.1、定義方法

> 方法的返回值類型可以不寫,編譯器可以自動推斷出來,但是對于遞歸函數,必須指定返回類型
#### 1.7.2、定義函數

#### 1.7.3、方法和函數的區別
> 在函數式編程語言中,函數是“頭等公民”,它可以像任何其他數據類型一樣被傳遞和操作
> 案例:首先定義一個方法,再定義一個函數,然后將函數傳遞到方法里面

~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/11.
*/
object MethodAndFunctionDemo {
//定義一個方法
//方法m2參數要求是一個函數,函數的參數必須是兩個Int類型
//返回值類型也是Int類型
def m1(f: (Int, Int) => Int) : Int = {
f(2, 6)
}
//定義一個函數f1,參數是兩個Int類型,返回值是一個Int類型
val f1 = (x: Int, y: Int) => x + y
//再定義一個函數f2
val f2 = (m: Int, n: Int) => m * n
//main方法
def main(args: Array[String]) {
//調用m1方法,并傳入f1函數
val r1 = m1(f1)
println(r1)
//調用m1方法,并傳入f2函數
val r2 = m1(f2)
println(r2)
}
}
~~~
#### 1.7.4、將方法轉換成函數(神奇的下劃線)

## 2、數組、映射、元組、集合
### 2.1、數組
#### 2.1.1、定長數組和變長數組
~~~
package cn.itcast.scala
import scala.collection.mutable.ArrayBuffer
/**
* Created by ZX on 2015/11/11.
*/
object ArrayDemo {
def main(args: Array[String]) {
//初始化一個長度為8的定長數組,其所有元素均為0
val arr1 = new Array[Int](8)
//直接打印定長數組,內容為數組的hashcode值
println(arr1)
//將數組轉換成數組緩沖,就可以看到原數組中的內容了
//toBuffer會將數組轉換長數組緩沖
println(arr1.toBuffer)
//注意:如果new,相當于調用了數組的apply方法,直接為數組賦值
//初始化一個長度為1的定長數組
val arr2 = Array[Int](10)
println(arr2.toBuffer)
//定義一個長度為3的定長數組
val arr3 = Array("hadoop", "storm", "spark")
//使用()來訪問元素
println(arr3(2))
//////////////////////////////////////////////////
//變長數組(數組緩沖)
//如果想使用數組緩沖,需要導入import scala.collection.mutable.ArrayBuffer包
val ab = ArrayBuffer[Int]()
//向數組緩沖的尾部追加一個元素
//+=尾部追加元素
ab += 1
//追加多個元素
ab += (2, 3, 4, 5)
//追加一個數組++=
ab ++= Array(6, 7)
//追加一個數組緩沖
ab ++= ArrayBuffer(8,9)
//打印數組緩沖ab
//在數組某個位置插入元素用insert
ab.insert(0, -1, 0)
//刪除數組某個位置的元素用remove
ab.remove(8, 2)
println(ab)
}
}
~~~
#### 2.1.2、遍歷數組
1. 增強for循環
2. 好用的until會生成腳標,0 until 10 包含0不包含10

~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/12.
*/
object ForArrayDemo {
def main(args: Array[String]) {
//初始化一個數組
val arr = Array(1,2,3,4,5,6,7,8)
//增強for循環
for(i <- arr)
println(i)
//好用的until會生成一個Range
//reverse是將前面生成的Range反轉
for(i <- (0 until arr.length).reverse)
println(arr(i))
}
}
~~~
#### 2.1.3數組轉換
> yield關鍵字將原始的數組進行轉換會產生一個新的數組,原始的數組不變

~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/12.
*/
object ArrayYieldDemo {
def main(args: Array[String]) {
//定義一個數組
val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
//將偶數取出乘以10后再生成一個新的數組
val res = for (e <- arr if e % 2 == 0) yield e * 10
println(res.toBuffer)
//更高級的寫法,用著更爽
//filter是過濾,接收一個返回值為boolean的函數
//map相當于將數組中的每一個元素取出來,應用傳進去的函數
val r = arr.filter(_ % 2 == 0).map(_ * 10)
println(r.toBuffer)
}
}
~~~
#### 2.1.4、數組常用算法
> 在Scala中,數組上的某些方法對數組進行相應的操作非常方便!

### 2.2、映射
> 在Scala中,把哈希表這種數據結構叫做映射
#### 2.2.1、構建映射

#### 2.2.2、獲取和修改映射中的值

> 好用的getOrElse

> 注意:在Scala中,有兩種Map,一個是immutable包下的Map,該Map中的內容不可變;另一個是mutable包下的Map,該Map中的內容可變
> 例子:

> 注意:通常我們在創建一個集合是會用val這個關鍵字修飾一個變量(相當于java中的final),那么就意味著該變量的引用不可變,該引用中的內容是不是可變,取決于這個引用指向的集合的類型
### 2.3、元組
> 映射是K/V對偶的集合,對偶是元組的最簡單形式,元組可以裝著多個不同類型的值。
#### 2.3.1、創建元組

#### 2.3.2、獲取元組中的值

#### 2.3.3、將對偶的集合轉換成映射

#### 2.3.4、拉鏈操作
> zip命令可以將多個值綁定在一起

> 注意:如果兩個數組的元素個數不一致,拉鏈操作后生成的數組的長度為較小的那個數組的元素個數
### 2.4、集合
> Scala的集合有三大類:序列Seq、集Set、映射Map,所有的集合都擴展自Iterable特質
> 在Scala中集合有可變(mutable)和不可變(immutable)兩種類型,immutable類型的集合初始化后就不能改變了(注意與val修飾的變量進行區別)
#### 2.4.1、序列
> 不可變的序列 import scala.collection.immutable._
> 在Scala中列表要么為空(Nil表示空列表)要么是一個head元素加上一個tail列表。
> 9 :: List(5, 2) :: 操作符是將給定的頭和尾創建一個新的列表
> 注意::: 操作符是右結合的,如9 :: 5 :: 2 :: Nil相當于 9 :: (5 :: (2 :: Nil))
~~~
package cn.itcast.collect
object ImmutListDemo {
def main(args: Array[String]) {
//創建一個不可變的集合
val lst1 = List(1,2,3)
//將0插入到lst1的前面生成一個新的List
val lst2 = 0 :: lst1
val lst3 = lst1.::(0)
val lst4 = 0 +: lst1
val lst5 = lst1.+:(0)
//將一個元素添加到lst1的后面產生一個新的集合
val lst6 = lst1 :+ 3
val lst0 = List(4,5,6)
//將2個list合并成一個新的List
val lst7 = lst1 ++ lst0
//將lst0插入到lst1前面生成一個新的集合
val lst8 = lst1 ++: lst0
//將lst0插入到lst1前面生成一個新的集合
val lst9 = lst1.:::(lst0)
println(lst9)
}
}
~~~
> 可變的序列 import scala.collection.mutable._
~~~
package cn.itcast.collect
import scala.collection.mutable.ListBuffer
object MutListDemo extends App{
//構建一個可變列表,初始有3個元素1,2,3
val lst0 = ListBuffer[Int](1,2,3)
//創建一個空的可變列表
val lst1 = new ListBuffer[Int]
//向lst1中追加元素,注意:沒有生成新的集合
lst1 += 4
lst1.append(5)
//將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
lst0 ++= lst1
//將lst0和lst1合并成一個新的ListBuffer 注意:生成了一個集合
val lst2= lst0 ++ lst1
//將元素追加到lst0的后面生成一個新的集合
val lst3 = lst0 :+ 5
}
~~~
### 2.5、Set
> 不可變的Set
~~~
package cn.itcast.collect
import scala.collection.immutable.HashSet
object ImmutSetDemo extends App{
val set1 = new HashSet[Int]()
//將元素和set1合并生成一個新的set,原有set不變
val set2 = set1 + 4
//set中元素不能重復
val set3 = set1 ++ Set(5, 6, 7)
val set0 = Set(1,3,4) ++ set1
println(set0.getClass)
}
~~~
> 可變的Set
~~~
package cn.itcast.collect
import scala.collection.mutable
object MutSetDemo extends App{
//創建一個可變的HashSet
val set1 = new mutable.HashSet[Int]()
//向HashSet中添加元素
set1 += 2
//add等價于+=
set1.add(4)
set1 ++= Set(1,3,5)
println(set1)
//刪除一個元素
set1 -= 5
set1.remove(2)
println(set1)
}
~~~
### 2.6、Map
~~~
package cn.itcast.collect
import scala.collection.mutable
object MutMapDemo extends App{
val map1 = new mutable.HashMap[String, Int]()
//向map中添加數據
map1("spark") = 1
map1 += (("hadoop", 2))
map1.put("storm", 3)
println(map1)
//從map中移除元素
map1 -= "spark"
map1.remove("hadoop")
println(map1)
}
~~~
## 3、類、對象、繼承、特質
> Scala的類與Java、C++的類比起來更簡潔,學完之后你會更愛Scala!!!
### 3.1、類
#### 3.1.1、類的定義
~~~
//在Scala中,類并不用聲明為public。
//Scala源文件中可以包含多個類,所有這些類都具有公有可見性。
class Person {
//用val修飾的變量是只讀屬性,有getter但沒有setter
//(相當與Java中用final修飾的變量)
val id = "9527"
//用var修飾的變量既有getter又有setter
var age: Int = 18
//類私有字段,只能在類的內部使用
private var name: String = "唐伯虎"
//對象私有字段,訪問權限更加嚴格的,Person類的方法只能訪問到當前對象的字段
private[this] val pet = "小強"
}
~~~
### 3.1.2、構造器
~~~
注意:主構造器會執行類定義中的所有語句
/**
*每個類都有主構造器,主構造器的參數直接放置類名后面,與類交織在一起
*/
class Student(val name: String, val age: Int){
//主構造器會執行類定義中的所有語句
println("執行主構造器")
try {
println("讀取文件")
throw new IOException("io exception")
} catch {
case e: NullPointerException => println("打印異常Exception : " + e)
case e: IOException => println("打印異常Exception : " + e)
} finally {
println("執行finally部分")
}
private var gender = "male"
//用this關鍵字定義輔助構造器
def this(name: String, age: Int, gender: String){
//每個輔助構造器必須以主構造器或其他的輔助構造器的調用開始
this(name, age)
println("執行輔助構造器")
this.gender = gender
}
}
~~~
~~~
/**
*構造器參數可以不帶val或var,如果不帶val或var的參數至少被一個方法所使用,
*那么它將會被提升為字段
*/
//在類名后面加private就變成了私有的
class Queen private(val name: String, prop: Array[String], private var age: Int = 18){
println(prop.size)
//prop被下面的方法使用后,prop就變成了不可變得對象私有字段,等同于private[this] val prop
//如果沒有被方法使用該參數將不被保存為字段,僅僅是一個可以被主構造器中的代碼訪問的普通參數
def description = name + " is " + age + " years old with " + prop.toBuffer
}
object Queen{
def main(args: Array[String]) {
//私有的構造器,只有在其伴生對象中使用
val q = new Queen("hatano", Array("蠟燭", "皮鞭"), 20)
println(q.description())
}
}
~~~
### 3.2、對象
#### 3.2.1、單例對象
> 在Scala中沒有靜態方法和靜態字段,但是可以使用object這個語法結構來達到同樣的目的
1. 存放工具方法和常量
2. 高效共享單個不可變的實例
3. 單例模式
~~~
package cn.itcast.scala
import scala.collection.mutable.ArrayBuffer
/**
* Created by ZX on 2015/11/14.
*/
object SingletonDemo {
def main(args: Array[String]) {
//單例對象,不需要new,用【類名.方法】調用對象中的方法
val session = SessionFactory.getSession()
println(session)
}
}
object SessionFactory{
//該部分相當于java中的靜態塊
var counts = 5
val sessions = new ArrayBuffer[Session]()
while(counts > 0){
sessions += new Session
counts -= 1
}
//在object中的方法相當于java中的靜態方法
def getSession(): Session ={
sessions.remove(0)
}
}
class Session{
}
~~~
#### 3.2.2、伴生對象
> 在Scala的類中,與類名相同的對象叫做伴生對象,類和伴生對象之間可以相互訪問私有的方法和屬性
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/14.
*/
class Dog {
val id = 1
private var name = "itcast"
def printName(): Unit ={
//在Dog類中可以訪問伴生對象Dog的私有屬性
println(Dog.CONSTANT + name )
}
}
/**
* 伴生對象
*/
object Dog {
//伴生對象中的私有屬性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new Dog
//訪問私有的字段name
p.name = "123"
p.printName()
}
}
~~~
#### 3.2.3、apply方法
> 通常我們會在類的伴生對象中定義apply方法,當遇到類名(參數1,...參數n)時apply方法會被調用
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/14.
*/
object ApplyDemo {
def main(args: Array[String]) {
//調用了Array伴生對象的apply方法
//def apply(x: Int, xs: Int*): Array[Int]
//arr1中只有一個元素5
val arr1 = Array(5)
println(arr1.toBuffer)
//new了一個長度為5的array,數組里面包含5個null
var arr2 = new Array(5)
}
}
~~~
#### 3.2.4、應用程序對象
> Scala程序都必須從一個對象的main方法開始,可以通過擴展App特質,不寫main方法。
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/14.
*/
object AppObjectDemo extends App{
//不用寫main方法
println("I love you Scala")
}
~~~
### 3.3、繼承
#### 3.3.1、擴展類
> 在Scala中擴展類的方式和Java一樣都是使用extends關鍵字
#### 3.3.2、重寫方法
> 在Scala中重寫一個非抽象的方法必須使用override修飾符
#### 3.3.3、類型檢查和轉換
|Scala | Java |
| --- | --- |
|obj.isInstanceOf[C] |obj instanceof C |
|obj.asInstanceOf[C] |(C)obj |
|classOf[C] | C.class |
#### 3.3.4、超類的構造
~~~
package cn.itcast.scala
/**
* Created by ZX on 2015/11/10.
*/
object ClazzDemo {
def main(args: Array[String]) {
//val h = new Human
//println(h.fight)
}
}
trait Flyable{
def fly(): Unit ={
println("I can fly")
}
def fight(): String
}
abstract class Animal {
def run(): Int
val name: String
}
class Human extends Animal with Flyable{
val name = "abc"
//打印幾次"ABC"?
val t1,t2,(a, b, c) = {
println("ABC")
(1,2,3)
}
println(a)
println(t1._1)
//在Scala中重寫一個非抽象方法必須用override修飾
override def fight(): String = {
"fight with 棒子"
}
//在子類中重寫超類的抽象方法時,不需要使用override關鍵字,寫了也可以
def run(): Int = {
1
}
}
~~~
## 4、模式匹配和樣例類
> Scala有一個十分強大的模式匹配機制,可以應用到很多場合:如switch語句、類型檢查等。
> 并且Scala還提供了樣例類,對模式匹配進行了優化,可以快速進行匹配
### 4.1、匹配字符串
~~~
package cn.itcast.cases
import scala.util.Random
object CaseDemo01 extends App{
val arr = Array("YoshizawaAkiho", "YuiHatano", "AoiSola")
val name = arr(Random.nextInt(arr.length))
name match {
case "YoshizawaAkiho" => println("吉澤老師...")
case "YuiHatano" => println("波多老師...")
case _ => println("真不知道你們在說什么...")
}
}
~~~
### 4.2、匹配類型
~~~
package cn.itcast.cases
import scala.util.Random
object CaseDemo01 extends App{
//val v = if(x >= 5) 1 else if(x < 2) 2.0 else "hello"
val arr = Array("hello", 1, 2.0, CaseDemo)
val v = arr(Random.nextInt(4))
println(v)
v match {
case x: Int => println("Int " + x)
case y: Double if(y >= 0) => println("Double "+ y)
case z: String => println("String " + z)
case _ => throw new Exception("not match exception")
}
}
~~~
> 注意:case y: Double if(y >= 0) => ...
> 模式匹配的時候還可以添加守衛條件。如不符合守衛條件,將掉入case _中
### 4.3、匹配數組、元組
~~~
package cn.itcast.cases
object CaseDemo03 extends App{
val arr = Array(1, 3, 5)
arr match {
case Array(1, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
case Array(0, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (2, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
~~~
> 注意:在Scala中列表要么為空(Nil表示空列表)要么是一個head元素加上一個tail列表。
> 9 :: List(5, 2) :: 操作符是將給定的頭和尾創建一個新的列表
> 注意::: 操作符是右結合的,如9 :: 5 :: 2 :: Nil相當于 9 :: (5 :: (2 :: Nil))
### 4.4、樣例類
> 在Scala中樣例類是一中特殊的類,可用于模式匹配。case class是多例的,后面要跟構造參數,case object是單例的
~~~
package cn.itcast.cases
import scala.util.Random
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
object CaseDemo04 extends App{
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => {
println(s"$id, $name")
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
~~~
### 4.5、Option類型
> 在Scala中Option類型樣例類用來表示可能存在或也可能不存在的值(Option的子類有Some和None)。Some包裝了某個值,None表示沒有值
~~~
package cn.itcast.cases
object OptionDemo {
def main(args: Array[String]) {
val map = Map("a" -> 1, "b" -> 2)
val v = map.get("b") match {
case Some(i) => i
case None => 0
}
println(v)
//更好的方式
val v1 = map.getOrElse("c", 0)
println(v1)
}
}
~~~
### 4.6、偏函數
> 被包在花括號內沒有match的一組case語句是一個偏函數,它是PartialFunction[A, B]的一個實例,A代表參數類型,B代表返回類型,常用作輸入模式匹配
~~~
package cn.itcast.cases
object PartialFuncDemo {
def func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
}
}
~~~
## 5、高階函數
### 5.1、概念
> Scala混合了面向對象和函數式的特性,我們通常將可以做為參數傳遞到方法中的表達式叫做函數。在函數式編程語言中,函數是“頭等公民”,高階函數包含:作為值的函數、匿名函數、閉包、柯里化等等。
### 5.2、作為值的函數
> 可以像任何其他數據類型一樣被傳遞和操作的函數,每當你想要給算法傳入具體動作時這個特性就會變得非常有用。

> 定義函數時格式:
* val 變量名 = (輸入參數類型和個數) => 函數實現和返回值類型和個數
* “=”表示將函數賦給一個變量
* “=>”左面表示輸入參數名稱、類型和個數,右邊表示函數的實現和返回值類型和參數個數
### 5.3、匿名函數
> 在Scala中,你不需要給每一個函數命名,沒有將函數賦給變量的函數叫做匿名函數

### 5.4、將方法轉換成函數
> 在Scala中,方法和函數是不一樣的,最本質的區別是函數可以做為參數傳遞到方法中
但是方法可以被轉換成函數,神奇的下劃線又出場了

### 5.5、柯里化
> 柯里化指的是將原來接受兩個參數的方法變成新的接受一個參數的方法的過程

### 5.6、例子
~~~
package cn.itcast.scala
object FunDemo {
def main(args: Array[String]) {
def f2(x: Int) = x * 2
val f3 = (x: Int) => x * 3
val f4: (Int) => Int = { x => x * 4 }
val f4a: (Int) => Int = _ * 4
val f5 = (_: Int) * 5
val list = List(1, 2, 3, 4, 5)
var new_list: List[Int] = null
//第一種:最直觀的方式 (Int) => Int
//new_list = list.map((x: Int) => x * 3)
//第二種:由于map方法知道你會傳入一個類型為(Int) => Int的函數,你可以簡寫
//new_list = list.map((x) => x * 3)
//第三種:對于只有一個參數的函數,你可以省去參數外圍的()
//new_list = list.map(x => x * 3)
//第四種:(終極方式)如果參數在=>右側只出現一次,可以使用_
new_list = list.map(_ * 3)
new_list.foreach(println(_))
var a = Array(1,2,3)
a.map(_* 3)
}
}
~~~
## 6、隱式轉換和隱式參數
### 6.1、概念
> 隱式轉換和隱式參數是Scala中兩個非常強大的功能,利用隱式轉換和隱式參數,你可以提供優雅的類庫,對類庫的使用者隱匿掉那些枯燥乏味的細節。
### 6.2、作用
> 隱式的對類的方法進行增強,豐富現有類庫的功能
### 6.3、隱式轉換函數
> 是指那種以implicit關鍵字聲明的帶有單個參數的函數
### 6.4、隱式轉換例子
~~~
package cn.itcast.impli
import java.io.File
import scala.io.Source
//隱式的增強File類的方法
class RichFile(val from: File) {
def read = Source.fromFile(from.getPath).mkString
}
object RichFile {
//隱式轉換方法
implicit def file2RichFile(from: File) = new RichFile(from)
}
object MainApp{
def main(args: Array[String]): Unit = {
//導入隱式轉換
import RichFile._
//import RichFile.file2RichFile
println(new File("c://words.txt").read)
}
}
package cn.itcast.scala
import java.awt.GridLayout
/**
* Created by ZX on 2015/11/13.
*/
object ImplicitContext{
//implicit def girl2Ordered(g : Girl) = new Ordered[Girl]{
// override def compare(that: Girl): Int = if (g.faceValue > that.faceValue) 1 else -1
//}
implicit object OrderingGirl extends Ordering[Girl]{
override def compare(x: Girl, y: Girl): Int = if (x.faceValue > y.faceValue) 1 else -1
}
}
class Girl(var name: String, var faceValue: Double){
override def toString: String = s"name : $name, faveValue : $faceValue"
}
//class MissRight[T <% Ordered[T]](f: T, s: T){
// def choose() = if(f > s) f else s
//}
//class MissRight[T](f: T, s: T){
// def choose()(implicit ord: T => Ordered[T]) = if (f > s) f else s
//}
class MissRight[T: Ordering](val f: T, val s: T){
def choose()(implicit ord: Ordering[T]) = if(ord.gt(f, s)) f else s
}
object MissRight {
def main(args: Array[String]) {
import ImplicitContext.OrderingGirl
val g1 = new Girl("yuihatano", 99)
val g2 = new Girl("jzmb", 98)
val mr = new MissRight(g1, g2)
val result = mr.choose()
println(result)
}
}
~~~
## 7、Scala Actor編程
### 7.1、概念
> Scala中的Actor能夠實現并行編程的強大功能,它是基于事件模型的并發機制,Scala是運用消息(message)的發送、接收來實現多線程的。使用Scala能夠更容易地實現多線程應用的開發。
### 7.2、傳統java并發編程與Scala Actor編程的區別

> 對于Java,我們都知道它的多線程實現需要對共享資源(變量、對象等)使用synchronized 關鍵字進行代碼塊同步、對象鎖互斥等等。而且,常常一大塊的try…catch語句塊中加上wait方法、notify方法、notifyAll方法是讓人很頭疼的。原因就在于Java中多數使用的是可變狀態的對象資源,對這些資源進行共享來實現多線程編程的話,控制好資源競爭與防止對象狀態被意外修改是非常重要的,而對象狀態的不變性也是較難以保證的。 而在Scala中,我們可以通過復制不可變狀態的資源(即對象,Scala中一切都是對象,連函數、方法也是)的一個副本,再基于Actor的消息發送、接收機制進行并行編程
### 7.3、Actor方法執行順序
1. 首先調用start()方法啟動Actor
2. 調用start()方法后其act()方法會被執行
3. 向Actor發送消息
### 7.4、發送消息的方式
| ! | 發送異步消息,沒有返回值。|
| --- | --- |
| !? | 發送同步消息,等待返回值。|
| !!| 發送異步消息,返回值是 Future[Any]。|
## 8、Actor實戰
### 8.1、第一個例子
~~~
package cn.itcast.actor
//注意導包是scala.actors.Actor
import scala.actors.Actor
object MyActor1 extends Actor{
//重新act方法
def act(){
for(i <- 1 to 10){
println("actor-1 " + i)
Thread.sleep(2000)
}
}
}
object MyActor2 extends Actor{
//重新act方法
def act(){
for(i <- 1 to 10){
println("actor-2 " + i)
Thread.sleep(2000)
}
}
}
object ActorTest extends App{
//啟動Actor
MyActor1.start()
MyActor2.start()
}
~~~
* 說明:上面分別調用了兩個單例對象的start()方法,他們的act()方法會被執行,相同與在java中開啟了兩個線程,線程的run()方法會被執行
* 注意:這兩個Actor是并行執行的,act()方法中的for循環執行完成后actor程序就退出了
### 8.2、第二個例子(可以不斷地接收消息)
~~~
package cn.itcast.actor
import scala.actors.Actor
/**
* Created by ZX on 2016/4/4.
*/
class MyActor extends Actor {
override def act(): Unit = {
while (true) {
receive {
case "start" => {
println("starting ...")
Thread.sleep(5000)
println("started")
}
case "stop" => {
println("stopping ...")
Thread.sleep(5000)
println("stopped ...")
}
}
}
}
}
object MyActor {
def main(args: Array[String]) {
val actor = new MyActor
actor.start()
actor ! "start"
actor ! "stop"
println("消息發送完成!")
}
}
~~~
* 說明:在act()方法中加入了while (true) 循環,就可以不停的接收消息
* 注意:發送start消息和stop的消息是異步的,但是Actor接收到消息執行的過程是同步的按順序執行
### 8.3、第三個例子(react方式會復用線程,比receive更高效)
~~~
package cn.itcast.actor
import scala.actors.Actor
/**
* Created by ZX on 2016/4/4.
*/
class YourActor extends Actor {
override def act(): Unit = {
loop {
react {
case "start" => {
println("starting ...")
Thread.sleep(5000)
println("started")
}
case "stop" => {
println("stopping ...")
Thread.sleep(8000)
println("stopped ...")
}
}
}
}
}
object YourActor {
def main(args: Array[String]) {
val actor = new YourActor
actor.start()
actor ! "start"
actor ! "stop"
println("消息發送完成!")
}
}
~~~
* 說明: react 如果要反復執行消息處理,react外層要用loop,不能用while
### 8.4、第四個例子(結合case class發送消息)
~~~
package cn.itcast.actor
package cn.itcast.actor
import scala.actors.Actor
class AppleActor extends Actor {
def act(): Unit = {
while (true) {
receive {
case "start" => println("starting ...")
case SyncMsg(id, msg) => {
println(id + ",sync " + msg)
Thread.sleep(5000)
sender ! ReplyMsg(3,"finished")
}
case AsyncMsg(id, msg) => {
println(id + ",async " + msg)
Thread.sleep(5000)
}
}
}
}
}
object AppleActor {
def main(args: Array[String]) {
val a = new AppleActor
a.start()
//異步消息
a ! AsyncMsg(1, "hello actor")
println("異步消息發送完成")
//同步消息
//val content = a.!?(1000, SyncMsg(2, "hello actor"))
//println(content)
val reply = a !! SyncMsg(2, "hello actor")
println(reply.isSet)
//println("123")
val c = reply.apply()
println(reply.isSet)
println(c)
}
}
case class SyncMsg(id : Int, msg: String)
case class AsyncMsg(id : Int, msg: String)
case class ReplyMsg(id : Int, msg: String)
~~~
### 8.5、練習
> 用actor并發編程寫一個單機版的WorldCount,將多個文件作為輸入,計算完成后將多個任務匯總,得到最終的結果
~~~
package cn.itcast.actor
import java.io.File
import scala.actors.{Actor, Future}
import scala.collection.mutable
import scala.io.Source
/**
* Created by ZX on 2016/4/4.
*/
class Task extends Actor {
override def act(): Unit = {
loop {
react {
case SubmitTask(fileName) => {
val contents = Source.fromFile(new File(fileName)).mkString
val arr = contents.split("\r\n")
val result = arr.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.length)
//val result = arr.flatMap(_.split(" ")).map((_, 1)).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))
sender ! ResultTask(result)
}
case StopTask => {
exit()
}
}
}
}
}
object WorkCount {
def main(args: Array[String]) {
val files = Array("c://words.txt", "c://words.log")
val replaySet = new mutable.HashSet[Future[Any]]
val resultList = new mutable.ListBuffer[ResultTask]
for(f <- files) {
val t = new Task
val replay = t.start() !! SubmitTask(f)
replaySet += replay
}
while(replaySet.size > 0){
val toCumpute = replaySet.filter(_.isSet)
for(r <- toCumpute){
val result = r.apply()
resultList += result.asInstanceOf[ResultTask]
replaySet.remove(r)
}
Thread.sleep(100)
}
val finalResult = resultList.map(_.result).flatten.groupBy(_._1).mapValues(x => x.foldLeft(0)(_ + _._2))
println(finalResult)
}
}
case class SubmitTask(fileName: String)
case object StopTask
case class ResultTask(result: Map[String, Int])
~~~
- hadoop
- linux基礎
- Linux入門
- Linux進階
- shell
- Zookeeper
- Zookeeper簡介及部署
- Zookeeper使用及API
- Redis
- Redis簡介安裝部署
- Redis使用及API
- Java高級增強
- Java多線程增強
- Maven簡介及搭建
- Hive
- Hive簡介及安裝
- Hive操作
- HIve常用函數
- Hive數據類型
- Flume
- Flume簡介及安裝
- flume 攔截器(interceptor)
- azkaban
- azKaban簡介及安裝
- Sqoop
- Sqoop簡介及安裝
- HDFS
- HDFS原理
- HDFS操作API
- MAPREDUCE原理
- MAPREDUCE圖片資源
- MAPREDUCE加強
- HBASE
- HBASE簡介及安裝
- HBASE操作及API
- HBASE內部原理
- Storm
- Storm簡介及安裝
- Storm原理
- kafka
- kafka簡介及安裝
- kafka常用操作及API
- kafka原理
- kafka配置詳解
- Scala
- Scala簡介及安裝
- Scala基礎語法
- Scala實戰