笔记内容选自慕课网《大数据开发工程师》体系课
8.1 为什么要学习Scala语言?
- Spark就是由Scala实现的,虽然也支持Java实现
- Java和Scala实现不同
「总结:你不学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
- 创建maven项目,安装Scala插件
- 引入Scala
- 编写main方法
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
<a name="dIdiX"></a>
### 8.3.2 数据类型
- Scala中的数据类型可以分为两种
- **基本数据类型有**
- Byte、Char、Short、Int、Long、Float、Double、Boolean
- **增强版数据类型有**
- StringOps、RichInt、RichDouble、RichChar 等
```scala
// 基本数据类型可以直接调用增强版数据类型的方法「隐式转换」
var a = 1.to(10)
var b = 1 to 10
// 变量3隐式转换为字符串的3
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
- List代表一个不可变的列表
- List的head、 tail以及 :: 操作符
- 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 }
- 主constructor
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通常会用于模式匹配中
// 判断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了」