笔记内容选自慕课网《大数据开发工程师》体系课

8.1 为什么要学习Scala语言?

  1. Spark就是由Scala实现的,虽然也支持Java实现
  2. Java和Scala实现不同

image.png
image.png
「总结:你不学Scala,要用Java实现Spark计算的话,代码量就会很大」
_

8.2 什么是Scala?

  • Scala是一门多范式的编程语言
    • 它是一种类似Java的编程语言,它设计的初衷是为了实现可伸缩的语言并集成面向对象编程和函数式编程的各种特性
  • Scala基于Java虚拟机,也就是基于JVM的一门编程语言
    • 所有Scala代码,都需要编译为字节码,然后交由Java虚拟机来运行
  • Scala和Java可以无缝相互操作
    • Scala可以任意调用Java代码,这个特性是非常好的

8.2.1 安装Scala

  • brew install scala@2.12

image.png

  • 创建maven项目,安装Scala插件

image.png

  • 引入Scala

image.png
image.png

  • 编写main方法

image.png

8.3 Scala的用法

8.3.1 变量

  • Scala中的变量分为两种:可变var不可变val
  • 在实际工作中,针对不需要改变值的变量,通常建议使用val
  • 无论声明val变量,还是声明var变量,都可以手动指定变量类型,如果不指定,Scala会自动根据值,进行类型推断 ```scala // 把1赋值给变量a var a = 1

// 把3赋值给不可变量b val b = 3

// 自定义类型 val c: Char = 1

  1. <a name="dIdiX"></a>
  2. ### 8.3.2 数据类型
  3. - Scala中的数据类型可以分为两种
  4. - **基本数据类型有**
  5. - Byte、Char、Short、Int、Long、Float、Double、Boolean
  6. - **增强版数据类型有**
  7. - StringOps、RichInt、RichDouble、RichChar 等
  8. ```scala
  9. // 基本数据类型可以直接调用增强版数据类型的方法「隐式转换」
  10. var a = 1.to(10)
  11. var b = 1 to 10
  12. // 变量3隐式转换为字符串的3
  13. var c = 3.toString

8.3.3 操作符

  • 比如 +、-、*、/、% 等,以及 &、|、^、>>、<< 等
  • 注意:Scala中没有提供++、–操作符
    // count自增1
    var count += 1
    

8.3.4 if表达式

// if判断年龄
var age = 20
println(if (age > 18) 1 else 2)

// 可以把判断结果赋值给变量
val res = if(age > 18) 1 else 0

// 如果if的返回值类型不一样,则会取两个类型的公共父类型
if(age > 18) "old" else 0
Any = old

// 如果if后面没有跟else,则默认else的值是Unit,也可以用()表示,类似于java中的void或者null
var age = 12
if(age > 18) “old” else ()
Any = ()

8.3.5 语句终结符

  • Scala默认不需要语句终结符,它将每一行作为一个语句
  • 如果一行要放多条语句,则前面的语句必须使用语句终结符
    val age = 20; if(age > 18) 1 else 0
    

8.3.6 for循环

// 开区间循环,从1循环到5
val n = 5
for(i <- 1 to n) 
    println(i)
1
2
3
4
5

// 闭区间循环,从1循环到4
val n = 5 
for(i <- 1 until n) 
    println(i)
1
2
3
4

// 循环字符串
for(c <- "jade") println(c)
j
a
d
e

// 如果多行,则需要花括号
for(i <- 1 to 5) { 
  println(i)
  println("hehe") 
}

8.3.7 while循环

// 倒计时五秒
var n = 5
while(n>0){ 
  println(n+"秒") 
  n -= 1 
}

5秒
4秒
3秒
2秒
1秒

8.3.8 高级for循环

  • if守卫模式
    • 把if判断写进for括号里
  • for推导式
    • 使用yield指定一个规则,对迭代出来的数字进行处理,并且创建一个新的集合 ```scala // if守卫模式 for(i <- 1 to 10 if i % 2 == 0) println(i) 2 4 6 8 10

// 根据规则迭代个新的集合 var list = for (i <- 1 to 5) yield i * 2 for (elem <- list) { println(elem) }


<a name="JjZot"></a>
## 8.4 集合

- Scala中的集合是分成可变和不可变两类集合的
   - **可变集合**
      - 集合的元素可以动态修改
      - 在 **scala.collection.mutable** 这个包下面
   - **不可变集合**
      - 集合的元素在初始化之后,就无法修改了,再添加元素相当于新建集合
      - 在 **scala.collection.immutable** 这个包下面

<a name="IqSwE"></a>
### 8.4.1 Set
遇事不决,查文档:[https://www.scala-lang.org/api/2.11.12/#package](https://www.scala-lang.org/api/2.11.12/#package)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1580562/1629165305061-029caf66-9dd5-48f6-b223-cd9a89c6404e.png#align=left&display=inline&height=550&margin=%5Bobject%20Object%5D&name=image.png&originHeight=550&originWidth=1267&size=169487&status=done&style=shadow&width=1267)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1580562/1629165429866-6c6185fa-4714-4a7c-89e6-54a5788b4ed2.png#align=left&display=inline&height=628&margin=%5Bobject%20Object%5D&name=image.png&originHeight=628&originWidth=1797&size=189844&status=done&style=shadow&width=1797)

- 只要前面带有**object**的,可以直接创建对象,并且**不需要使用new关键字**,所以set可以直接使用
- Set前面带有**T**的,意思是接口
- HashSet前面带有**C**的,意思是类
   - HashSet继承了Set的特性
- **Set**常用子类有
   - **HashSet**
      - 集合中的元素不重复、无序
      - 底层是哈希表
   - **LinkedHashSet**
      - 集合中的元素不重复、有序,它会用一个链表维护插入顺序, 可以保证集合中元素是有序的
      - 底层是链表
   - **SortedSet**
      - 集合中的元素不重复、有序,它会自动根据元素来进行排序
      - 底层是树

```scala
// 可变的Set
var s = mutable.Set(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 不可变的Set
var s = immutable.Set(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 可变的HashSet
var s = mutable.HashSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 不可变的HashSet
var s = immutable.HashSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 可变的LinkHashSet
var s = mutable.LinkedHashSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 不可变的LinkHashSet
var s = immutable.LinkedHashSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 可变的TreeSet
var s = mutable.TreeSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

// 不可变的TreeSet
var s = immutable.TreeSet(1,2,3)
    s += 50
    s += 100
  for (elem <- s) {
    println(elem)
  }

8.4.2 List

  • image.png
  • List代表一个不可变的列表
  • List的headtail以及 :: 操作符
  • ListBuffer:可以支持动态增加或者移除元素
// list的基本用法,不可变的列表,新增都会创建新列表
var l = List(1,2,3,4)
    println(l.head)
    println(l.tail)
    println(l.head :: l.tail)
  for (elem <- l) {
    println(elem)
  }

// ListBuffer,可以动态增删
var l = mutable.ListBuffer(1,2,3,4)
    l += 50
    l += 100
    println(l.head)
    println(l.tail)
  for (elem <- l) {
    println(elem)
  }

8.4.3 Map

  • Map是一种可迭代的键值对(key/value)结构
  • Map分为可变和不可变
    • 默认情况下使用的是不可变Map
  • HashMap
    • 是一个按照key的hash值进行排列存储的map
  • SortedMap
    • 可以自动对Map中的key进行排序【有序的map】
    • 不可变
  • LinkedHashMap
    • 按照的key-value插入的顺序来排序
    • 可变
// 创建不可变的map
var m = scala.collection.immutable.Map("jack"->30,"tom"->25,"jessic"->23)

// 创建可变的map
var m = scala.collection.mutable.Map(("jack",30),("tom",25),("jessic"->23))
    // 判断取值,有Jack就是30,没有就是0
    println(m.getOrElse("jack",0))

    // 修改元素
    m("jack") = 100

    // 新增元素
    m += ("hehe" -> 35, "haha" -> 40)

    // 移除元素
    m -= "hehe"

    //遍历key,value
    for ((key,value) <- m) {
      println(key + ":" + value)
    }

    //遍历key
    for (key <- m.keys) {
      println(key)
    }

    // 遍历value
    for (value <- m.values) {
      println(value)
    }

// 不可变的SortedMap
val ages = scala.collection.immutable.SortedMap("b" -> 30, "a" -> 15,"c"->25)

// 可变的LinkedHashMap
val ages = new scala.collection.mutable.LinkedHashMap[String, Int]()
ages("b")=30
ages("a")=15
ages("c")=25

8.4.4 Array

  • ScalaArray中的含义与Java中的数组类似,长度不可变
  • 由于Scala与Java都是运行在VM中,双方可以互相调用,因此 Scala数组的底层实际上就是Java数组
  • 数组初始化后,长度就固定下来了,元素全部根据其类型初始化,也可以直接使用 Array()创建数组,元素类型自动推断 ```scala // 定义5个Int数组 val a = new ArrayInt a(0)=50 a(1)=100 for (elem <- a) {
    println(elem)
    
    }

// 定义多数据类型的数组 val b = Array(“hello”, 30, 100L) for (elem <- b) { println(elem) }


<a name="Bw76i"></a>
### 8.4.5 ArrayBuffer

- Scala中ArrayBuffer与Java中的ArrayList类似,**长度可变**
```scala
// 动态初始化
val b = new ArrayBuffer[Int]()
    b += 1
    b += (2, 3, 4, 5)
    for (elem <- b) {
      println(elem)
    }

// 静态初始化
val b = ArrayBuffer(1,2,3,4)
    b += 5
    b += (6, 7, 8, 9)
    for (elem <- b) {
      println(elem)
    }

1、添加元素

  • 使用+=操作符
    • 可以添加一个元素
      • b += 1
    • 或者多个元素
      • b += (2, 3, 4, 5)
  • insert() 函数
    • 这种操作效率很低,因为需要移动指定位置后的所有元素
    • 第三个元素后面插入30
      • b.insert(3,30)

2、移除元素

  • 使用-=操作符
    • 可以添加一个元素
      • b -= 1
    • 或者多个元素
      • b -= (2, 3, 4, 5)
  • remove() 函数
    • 可以移除指定位置的元素
    • 移除第一个元素
      • b.remove(1)

3、:Array与ArrayBuffer转换

  • ArrayBuffer转Array
    • a.toArray
  • Array转ArrayBuffer
    • b.toBuffer

4、数组常见操作

// 根据角标遍历
val b = ArrayBuffer(1,2,3,4)
    b += (6, 7, 8, 9)
    // 移除最后一位,元素9
    b.remove(7)
    for (elem <- 0 until b.length) {
      println(b(elem))
    }

// 求和、求最大值
val a = Array(3, 2, 1, 4, 5)
// 求和
val sum = a.sum
// 最大值
val max = a.max

// 数组排序
val b = ArrayBuffer(4,7,10,5)
    b += (1, 12, 22, 100)
    val c = b.sorted
    for (elem <- c) {
      println(elem)
    }

8.4.6 Tuple

  • Tuple:称之为元组
    • 它与Array类似,都是不可变的
    • 但与数组不同的是元组可以包含不同类型的元素,Tuple中的元素角标从 1 开始
    • 目前 Scala 支持的元组最大长度为 22 ,对于更大长度可以使用集合或数组
      val t = (1, 3.14, "hehe")
      println(t._3)
      

8.4.7 总结

  • 可变集合
    • LinkedHashSet、ListBuffer、ArrayBuffer、LinkedHashMap
  • 不可变集合
    • List、SortedMap
  • 可变+不可变集合
    • Set、HashSet、SortedSet、Map、HashMap
  • 还有两个编外人员:
  • Array、Tuple
    • Array:长度不可变,里面的元素可修改
    • Tuple:长度不可变,里面的元素也不可修改

8.5 函数

  • 在 Scala中定义函数需要使用 def 关键字,函数包括函数名、参数、函数体
  • Scala要求必须给出函数所有参数的类型,但是函数返回值的类型不是必须的
  • 函数中最后一行代码的返回值就是整个函数的返回值,不需要使用 return,这一点与ava不同
    // 基本函数实现
    def Student(name:String,age:Int): Unit ={
    println("姓名:"+name+","+"年龄:"+age)
    }
    Student("张三",18)
    

8.5.1 函数的参数

  • 默认参数
    • 有时我们调用某些函数时,不希望给出参数的具体值,而希望使用参数自身默认的值
  • 带名参数
    • 在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递
  • 可变参数
    • 有时我们需要将函数定义为参数个数可变的形式,则此时可以使用可变参数定义函数

1、默认参数

// 带默认参数的函数
def sayHello(fName: String, mName: String = "mid", lName: String = "last") = {
  "第一个值:"+fName+","+"第二个值:"+mName+","+"第三个值:"+lName
}
println(sayHello("李四"))

2、带名参数

// 带名参数
def sayHello(fName: String, mName: String = "mid", lName: String = "last") = {
  "第一个值:"+fName+","+"第二个值:"+mName+","+"第三个值:"+lName
}
println(sayHello(fName = "100", mName = "200", lName = "300"))

3、可变参数

// 可变参数的Int*
def sum(nums: Int*) = {
      var res = 0
          // 遍历sums里的int
      for (num <- nums) {
        // 把每个值累加起来
        res += num
      }
          // 返回结果
      res
    }

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

8.5.2 特殊的函数-过程

// 这两种有返回值,称之为「函数」
def sayHello(name: String) = "Hello, " + name 
def sayHello(name: String): String = "Hello, " + name 

// 这两种没返回值,称之为「过程」
def sayHello(name: String) { "Hello, " + name } 
def sayHello(name: String): Unit = "Hello, " + name

8.5.3 lazy

  • Scala提供了lazy特性,如果将一个变量声明为lazy
    • 则只有在第一次使用该变量时,变量对应的表达式才会发生计算
  • 什么场景下需要使用lazy特性呢?
    • 这种特性对于特别耗时的操作特别有用
    • 比如打开文件这个操作
      // 去找磁盘里的文件,它是懒加载,当你不调用就不会去找
      lazy val lines = fromFile("D://test.txt").mkString
      

8.6 面向对象

    • class
  • 对象
    • object
  • 接口
    • trait

8.6.1、类-class

  • Scala中定义类和Java一样
    • 都是使用 class 关键字
    • 使用new关键字创建对象
  • Scala类中的构造函数可以分为主构造函数和辅助构造函数
    • 主constructor
      • 类似Java的默认构造函数 this()
    • 辅助constructor
      • 类似Java的重载构造函数 this(name,age) ```scala // 创建个人类 class Person { var name = “张三” def sayHello() { println(“Hello,” + name) } def getName = name }

val p = new Person() p.sayHello() println(“名字:”+p.getName)

<a name="hsTA3"></a>
#### 
<a name="BkrvG"></a>
### 8.6.2、主constructor

- Scala的主constructor是与类名放在一起的
- 与Java不同,Java中的构造函数是写在类内部的
```scala
// 主构造器
class Student(val name: String, val age: Int) {
  println("your name is " + name + ", your age is " + age)
}
new Student("zs",19)

8.6.3、辅助constructor

  • 可以给类定义多个辅助constructor
    • 类似于java中的构造函数重载
  • 辅助constructor之间可以互相调用
    • 但是第一行必须调用主constructor ```scala // 给学生指定姓名和年龄信息 class Student { var name = “jack” var age = 10 def this(name: String) { this() this.name = name } def this(name: String, age: Int) { this(name) this.age = age } }

val s1 = new Student(“tom”) println(s1.name) val s2 = new Student(“mick”,30) println(s2.name+”,”+s2.age)


<a name="W5vGb"></a>
### 8.6.4、对象-object

-  object:相当于class的单个实例,通常在里面放一些静态的field或者method
- object不能定义带参数constructor,只有空参constructor
- 第一次调用object的方法时,会执行object的constructor的仅执行一次
- object通常作为单例模式的实现,或者放class的静态成员
- object不能new,可以直接使用
```scala
// 有点像Java静态类,定义了可以直接调用
object Person {
  var age = 1
  println ("this Person object!")
  def getAge = age
}
println(Person.age)
println(Person.getAge)

8.6.5、伴生对象

  • class同名的object
    • object是class的伴生对象
    • class是object的伴生类
  • 伴生类和伴生对象必须存放在一个.scala文件之中
  • 伴生类和伴生对象最大特点在于可以互相访问private field ```scala // 创建一个Person object和Person class object Person { private val fdNum = 1 def getFdNum = fdNum } class Person(val name: String, val age: Int) { def sayHello = println(“Hi, “ + name + “,you are “ + age + “ years old!” +”,No.”+Person.fdNum) }

new Person(“tom”,20).sayHello println(Person.getFdNum)


<a name="07J6K"></a>
### 8.6.6、apply

-  apply是object中非常重要的一个特殊方法
   - 通常在伴生对象中实现
   - apply方法,**实现构造伴生类对象的功能**
- 在创建对象时,就不需要使用 new Class 的方式,而是使用Class()的方式
   - **隐式调用伴生对象的apply方法**,这样会让对象创建更加简洁
```scala
class Person(val name: String) {
  println("my name is," + name)
}
object Person {
  def apply(name: String) = {
    println("apply exec...")
    new Person(name)
  }
}

// new了class
new Person("tom")

// 实现了object
Person("tom")

8.6.7、main

object mainDemo {
  /**
   * 注意:main只能定义在object中,不能定义在class中
   * @param args
   */
  def main(args: Array[String]): Unit = {
    println("hello scala main")
  }
}

8.6.8、trait

  • Scala中的trait类似于ava中的interface
  • 在 triat 中可以定义抽象方法
  • 类可以使用 extends 关键字继承 trait
    • 无论继承类还是 trait 统一都是 extends
  • 类继承 trait 后
    • 必须实现 trait 中的抽象方法,实现时不需要使用 override关键字
  • scala不支持对类进行多继承,但是支持对 trait 进行多重继承,使用with关键字即可

    object PersonDemo {
    def main(args: Array[String]): Unit = {
      val p1 = new Person("tom")
      val p2 = new Person("jack")
      p1.sayHello(p2.name)
      p1.makeFriends(p2)
    }
    
    trait HelloTrait {
      def sayHello(name: String)
    }
    trait MakeFriendsTrait {
      def makeFriends(p: Person)
    }
    class Person(val name: String) extends HelloTrait with MakeFriendsTrait {
      def sayHello(name: String):Unit = {
        println("Hello, " + name)
      }
      def makeFriends(p: Person):Unit = {
        println("Hello, my name is " + name + ", your name is "+p.name)
      }
    }
    }
    

8.7 函数式编程

  • Scala是一门既面向对象,又面向过程的语言
  • 在 Scala中,函数与类、对象一样,都是一等公民,同一级别

8.7.1 函数赋值给变量

  • Scala中的函数可以独立定义
    • 独立存在
    • 可以直接将函数作为值赋值给变量
  • Scala的语法规定
    • 将函数赋值给变量时
    • 必须在函数后面加上空格和下划线 ```scala def sayHello(name: String) { println(“Hello, “ + name) } // 将函数赋值给变量 val sayHelloFunc = sayHello _

sayHelloFunc(“scala”) sayHello(“tom”)


<a name="yLWN0"></a>
### 8.7.2 匿名函数

-  Scala中的函数也可以不需要命名
   - 这种函数称为**匿名函数**
- 匿名函数的语法格式
   - **(参数名:参数类型)=>函数体**
- 可以将匿名函数直接**赋值给某个变量**
```scala
val sayHelloFunc = (name: String) => println("Hello, " + name)

8.7.3 高阶函数

  • 可以将某个函数作为参数传入其它函数
  • 接收其它函数作为当前函数的参数
    • 当前函数也被称作高阶函数(higher-order- function)
  • 高阶函数可以自动推断出它里面函数的参数类型
    • 对于只有一个参数的函数,还可以省去小括号 ```scala // 匿名函数 val sayHelloFunc = (name: String) => println(“Hello, “ + name)

// 高阶函数 def greeting(func: (String) => Unit, name: String) { func(name) }

// 使用高阶函数 greeting(sayHelloFunc, “scala”)

// 可以直接传匿名函数 greeting((name: String) => println(“Hello, “ + name),”scala”)

// 例子 // 先定义一个高阶函数 def greeting(func: (String) => Unit, name: String) { func(name) } // 使用高阶函数:完整写法 greeting((name: String) => println(“Hello, “ + name), “scala”) // 使用高阶函数:高阶函数可以自动推断出参数类型,而不需要写明类型 greeting((name) => println(“Hello, “ + name), “scala”) // 使用高阶函数:对于只有一个参数的函数,还可以省去其小括号 greeting(name => println(“Hello, “ + name), “scala”)


<a name="eKAmV"></a>
### 8.7.4 常用高阶函数

- 上述自定义高阶函数工作场景不多,更多是使用已经有的高阶函数
   - **map**
      - 对传入的每个元素都进行处理,返回一个元素
   - **flatMap**
      - 对传入的每个元素都进行处理,返回一个或者多个元素
   - **foreach**
      - 对传入的每个元素都进行处理,但是没有返回值
   - **filter**
      - 对传入的每个元素都进行条件判断,如果返回true,则保留该元素,否则过滤掉该元素
   - **reduceLeft**
      - 从左侧元素开始,进行reduce操作

<a name="lKs5U"></a>
#### 1、map
```scala
// 匿名函数传入常用高阶函数里
Array(1, 2, 3, 4, 5).map(num=>{num * 2})

// 如果只有一个参数的话,可以简写为
Array(1, 2, 3, 4, 5).map(_ * 2)

2、flatMap

// 对元素处理
Array("hello you","hello me").flatMap(line=>line.split(" "))

// 如果只有一个参数的话,可以简写为
Array("hello you","hello me").flatMap(_.split(" "))

3、foreach

// 遍历数据,无返回值,常用于打印
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(num=>println(num))

// 可以简写为
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(println(_))
Array(1, 2, 3, 4, 5).map(_ * 2).foreach(println _)

4、filter

// 过滤判断
Array(1, 2, 3, 4, 5).filter(num=>num % 2 == 0)

//可以简写为
Array(1, 2, 3, 4, 5).filter(_ % 2 == 0)

5、reduceLeft

  • 先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推
    • 这个操作操作就相当于 1 + 2 + 3 + 4 + 5
  • spark中有一个reduce函数,和这个函数的效果一致 ```scala // 迭代相加 Array(1, 2, 3, 4, 5).reduceLeft((t1,t2)=>t1+t2)

// 可以间写为,注意:这里的两个代表是两个元素 Array(1, 2, 3, 4, 5).reduceLeft( + _)


<a name="PNGJh"></a>
#### 6、函数式编程案例实现

- 统计多个文本内的单词总数
- 创建2个文件
   - a.txt
      - 内容:hello you hello me
   - b.txt
      - 内容:hello hehe hello haha
```scala
// 使用List的伴生对象,将多个文件内的内容创建为一个List
val lines01 = scala.io.Source.fromFile("D://a.txt").mkString 
val lines02 = scala.io.Source.fromFile("D://b.txt").mkString

// 注意:下面这一行是核心代码,使用了链式调用的函数式编程
lines.flatMap(_.split( " ")).map((_, 1)).map(_._2).reduceLeft(_ + _)
  • lines.flatMap(_.split( “ “))
    • 表示对每一行数据使用空格进行切割,返回每一个单词
  • .map((_, 1))
    • 针对每一个单词,都转成tuple类型,tuple中的第1个元素是这个单词,第2个元素表示 单词出现的次数1
  • .map(_._2)
    • 迭代每一个tuple,获取tuple中的第2个元素
  • .reduceLeft( + )
    • 对前面获取到的元素进行累加求和

8.8 高级特性

8.8.1 模式匹配

  • Scala没有ava中的switch case语法
    • 但是 Scala 提供了更加强大的 match case 语法,即模式匹配
  • Java的 switch case仅能匹配变量的值
    • Scala的 match** case可以匹配各种情况**
    • 比如
      • 变量的类型
      • 集合的元素
      • 有值没值
  • 语法格式:变量 match{ case值 => 代码 }

**

1、对变量的值进行模式匹配

def demo1(day: Int) {
  day match {
    case 1 => println("Monday")
    case 2 => println("Tuesday")
    case 3 => println("Wednesday")
    case _ => println("none")
  }
}

「注意:match case中,只要一个case分支满足并处理了,就不会继续判断下一个case分支了,这 一点与Java不同,java的switch case需要用break停止向下执行」

2、变量类型的模式匹配

def processException(e: Exception) {
  e match {
    case e1: IllegalArgumentException => println("IllegalArgumentException")
    case e2: FileNotFoundException => println("FileNotFoundException " + e2)
    case e3: IOException => println("IOException " + e3)
    case _: Exception => println("Exception ")
  }
}

processException(new IllegalArgumentException())

3、案例:异常处理

try {
  val lines02 = scala.io.Source.fromFile("D://test02.txt").mkString
} catch {
  case ex: FileNotFoundException => println("no file")
  case ex: IOException => println("io exception")
  case ex: Exception => println("exception")
}

4、case class与模式匹配

  • case calss:称为样例类
    • 类似于Java中Java的,只定义 field
    • Scala自动提供
      • getter方法
      • setter方法
      • 没有method方法
  • case class的主构造函数接收的参数通常不需要使用var或val修饰
    • Scala会自动使用val修饰
  • Scala自动为case class定义了伴生对象,也就是object
    • 并且定义了 apply() 方法,该方法接收主构造函数中相同的参数并返回 case class 对象
class Person
case class Teacher(name: String, sub: String) extends Person
case class Student(name: String, cla: String) extends Person

def check(p: Person) {
  p match {
    case Teacher(name, sub) => println("Teacher, name is " + name + ", sub is " + sub)
    case Student(name, cla) => println(" Student, name is " + name + ", cla is " + cla)
    case _ => println("none")
  }
}
check(new Person())
check(new Teacher("zero","class2"))
check(new Student("tom","class1"))

5、Option与模式匹配

  • Option有两种值
    • Some->表示有值
    • None->表示没有值
  • Option通常会用于模式匹配中
    • 用于判断某个变量是有值还是没有值
    • 这比null更加简洁明了

      ```scala // map存值 val ages = Map(“jack” -> 18, “tom” -> 30, “jessic” -> 27)

// 判断map是否符合map里的值 def getAge(name: String) { val age = ages.get(name) age match { case Some(age) => println(“your age is “ + age) case None => println(“none”) } }

getAge(“jack”) getAge(“hehe”)

<a name="mhhYJ"></a>
### 
<a name="aIet0"></a>
### 8.8.2 隐式转换

-  Scala的隐式转换
   - **允许手动指定将某种类型的对象转换成其它类型的对象**
- Scala的隐式转换
   - 最核心的就是**定义隐式转换函数**,即 **implicit conversion function**
- 隐式转换函数与普通函数唯一的语法区别是要以 **implicit** 开头而且最好要**定义函数返回类型**
-  Scala默认**会自动使用两种隐式转换**
   - 1:源类型,或者目标类型的伴生对象里面的隐式转换函数
   - 2:当前程序作用域内可以用唯一标识符表示的隐式转换函数
- 如果隐式转换函数不在上述两种情况下的话
   - 那么就必须手动**使用 import 语法引入某个包下的隐式转换函数**
- 建议:仅仅在需要进行隐式转换的地方

<a name="CDKvJ"></a>
#### 1、案例:狗也能抓老鼠
```scala
class cat(val name: String) {
  def catchMouse() {
    println(name + " catch mouse")
  }
}
class dog(val name: String)

implicit def object2Cat(obj: Object): cat = {
  if (obj.getClass == classOf[dog]) {
    val dog = obj.asInstanceOf[dog]
    new cat(dog.name)
  }
  else Nil
}

val d = new dog("d1")
d.catchMouse()

「注意:后续在工作中一般很少需要我们自己去定义隐式转换函数,大部分的场景是我们只需要使用import导入对应的隐式转换函数就可以了,在这个案例中我们是自己手工实现了一个隐私转换函数,因为他们都在一个作用域内,所以就不需要import了」