scala学习
一、基础
1、scala简介
scala是运行在 JVM 上的多范式编程语言,同时支持面向对象编程和面向函数式编程
2、安装
scala程序运行依赖于java类库,必须要有java运行环境
解压scala压缩包
配置环境变量
打开控制台,输出scala -version 测试
3、声明变量
语法格式:
val/var 变量标识:变量类型 = 初始值
val 定义的是不可重新赋值的变量
var 定义的是可重新赋值的变量
使用类型推断来定义变量
scala可以自动根据变量的值来自动推断变量的类型,这样编写代码更加简洁。
scala> val name = "tom"
name: String = tom
惰性赋值
当有一些变量保存的数据较大时,但是不需要马上加载到JVM内存。可以使用惰性赋值来提高效率。
语法格式:
lazy val/var 变量名 = 表达式
4、字符串
scala提供多种定义字符串的方式,将来我们可以根据需要来选择最方便的定义方式。
双引号
val/var 变量名 = “字符串”
插值表达式
scala中,可以使用插值表达式来定义字符串,有效避免大量字符串的拼接。
val/var 变量名 = s"${变量/表达式}字符串"
三引号
如果有大段的文本需要保存,就可以使用三引号来定义字符串。
val/var 变量名 = """字符串1
字符串2"""
5、数据类型与操作符
数据类型
运算符
层次结构
6、条件表达式
条件表达式就是if表达式,if表达式可以根据给定的条件是否满足,根据条件的结果(真或假)决定执行对应的操作。
有返回值的if
scala> val sex = "male"
sex: String = male
scala> val result = if(sex == "male") 1 else 0
result: Int = 1
块表达式
scala中,使用{}表示一个块表达式
scala> val a = {
| println("1 + 1")
| 1 + 1
| }
7、循环
在scala中,可以使用for和while,但一般推荐使用for表达式,因为for表达式语法更简洁
for表达式
for(i <- 表达式/数组/集合) {
// 表达式
}
while循环
scala> var i = 1
i: Int = 1
scala> while(i <= 10) {
| println(i)
| i = i+1
| }
8、方法
定义方法
- 参数列表的参数类型不能省略
- 返回值类型可以省略,由scala编译器自动推断
- 返回值可以不写return,默认就是{}块表达式的值
def methodName (参数名:参数类型, 参数名:参数类型) : [return type] = {
// 方法体:一系列的代码
}
方法调用方式
- 后缀调用法
对象名.方法名(参数)
Math.abs(-1)
- 中缀调用法
对象名 方法名 参数
scala> Math abs -1
- 花括号调用法
Math.abs{
// 表达式1
// 表达式2
}
- 无括号调用法
如果方法没有参数,可以省略方法名后面的括号
def m3()=println("hello")
m3()
9、函数
定义函数
val 函数变量名 = (参数名:参数类型, 参数名:参数类型....) => 函数体
方法转换为函数
-
有时候需要将方法转换为函数,作为变量传递,就需要将方法转换为函数
-
使用
_
即可将方法转换为函数
10、数组
定长数组
- 定长数组指的是数组的长度是不允许改变的
- 数组的元素是可以改变的
// 通过指定长度定义数组
val/var 变量名 = new Array[元素类型](数组长度)
// 用元素直接初始化数组
val/var 变量名 = Array(元素1, 元素2, 元素3...)
变长数组
变长数组指的是数组的长度是可变的,可以往数组中添加、删除元素
import scala.collection.mutable.ArrayBuffer
val/var a = ArrayBuffer(元素1,元素2,元素3....)
添加,修改,删除元素
- 使用
+=
添加元素 - 使用
-=
删除元素 - 使用
++=
追加一个数组到变长数组
遍历数组
- 使用
for表达式
直接遍历数组中的元素
scala> for(i<-a) println(i)
- 使用
索引
遍历数组中的元素
scala> for(i <- 0 to a.length - 1) println(a(i))
数组常用算法
- 求和——sum方法
- 求最大值——max方法
- 求最小值——min方法
- 排序——sorted方法
11、元组
元组可以用来包含一组不同类型的值
定义元组
使用括号来定义元组
val/var 元组 = (元素1, 元素2, 元素3....)
使用箭头来定义元组(元组只有两个元素)
val/var 元组 = 元素1->元素2
访问元组
使用_1、_2、_3…来访问元组中的元素,_1表示访问第一个元素,依次类推
// 获取第一个元素
scala> a._1
12、列表
列表是scala中最重要的、也是最常用的数据结构。
- 可以保存重复的值
- 有先后顺序
不可变列表
不可变列表就是列表的元素、长度都是不可变的。
使用`List(元素1, 元素2, 元素3, ...)`来创建一个不可变列表
val/var 变量名 = List(元素1, 元素2, 元素3...)
使用`Nil`创建一个不可变的空列表
val/var 变量名 = Nil
使用`::`方法创建一个不可变列表,使用::拼接方式来创建列表,必须在最后添加一个**Nil**
val/var 变量名 = 元素1 :: 元素2 :: Nil
可变列表
可变列表就是列表的元素、长度都是可变的。
import scala.collection.mutable.ListBuffer
val/var 变量名 = ListBuffer(元素1,元素2,元素3...)
可变列表操作
- 获取元素(使用括号访问
(索引值)
) - 添加元素(
+=
) - 追加一个列表(
++=
) - 更改元素(
使用括号获取元素,然后进行赋值
) - 删除元素(
-=
) - 转换为List(
toList
) - 转换为Array(
toArray
)
列表常用操作
- 判断列表是否为空(
isEmpty
) - 拼接两个列表(
++
) - 获取列表的首个元素(
head
)和剩余部分(tail
) - 反转列表(
reverse
) - 获取前缀(
take
)、获取后缀(drop
) - 扁平化(
flaten
) - 拉链(
zip
)和拉开(unzip
) - 转换字符串(
toString
) - 生成字符串(
mkString
) - 并集(
union
) - 交集(
intersect
) - 差集(
diff
)
13、Set
Set(集)是代表没有重复元素的集合。
- 元素不重复
- 不保证插入顺序
不可变集
创建一个空的不可变集,语法格式:
val/var 变量名 = Set[类型]()
给定元素来创建一个不可变集,语法格式:
val/var 变量名 = Set(元素1, 元素2, 元素3...)
基本操作
- 获取集的大小(
size
) - 遍历集(
和遍历数组一致
) - 添加一个元素,生成一个Set(
+
) - 拼接两个集,生成一个Set(
++
) - 拼接集和列表,生成一个Set(
++
)
可变集
可变集合不可变集的创建方式一致,只不过需要提前导入一个可变集类。
手动导入:import scala.collection.mutable.Set
14、映射
Map可以称之为映射。它是由键值对组成的集合。
不可变Map
val/var map = Map(键->值, 键->值, 键->值...) // 推荐,可读性更好
val/var map = Map((键, 值), (键, 值), (键, 值), (键, 值)...)
可变Map
定义语法与不可变Map一致。但定义可变Map需要手动导入import scala.collection.mutable.Map
Map基本操作
- 获取值(
map(key)
) - 获取所有key(
map.keys
) - 获取所有value(
map.values
) - 遍历map集合
- 如果不存在则,getOrElse(
key
,返回值
) - 增加key,value对
- 删除key
15、iterator迭代器
scala针对每一类集合都提供了一个迭代器(iterator)用来迭代访问集合
scala> val ite = a.iterator
ite: Iterator[Int] = non-empty iterator
scala> while(ite.hasNext) {
| println(ite.next)
| }
16、函数式编程
- 遍历(
foreach
) - 映射(
map
) - 映射扁平化(
flatmap
) - 过滤(
filter
) - 是否存在(
exists
) - 排序(
sorted
、sortBy
、sortWith
) - 分组(
groupBy
) - 聚合计算(
reduce
) - 折叠(
fold
)
遍历 foreach
接收一个函数对象,函数的输入参数为集合的元素,返回值为空
// 定义一个列表
scala> val a = List(1,2,3,4)
a: List[Int] = List(1, 2, 3, 4)
// 迭代打印
scala> a.foreach((x:Int)=>println(x))
映射 map
map方法接收一个函数,将这个函数应用到每一个元素,返回一个新的列表
scala> a.map(x=>x+1)
res4: List[Int] = List(2, 3, 4, 5)
扁平化映射 flatMap
可以把flatMap,理解为先map,然后再flatten
- map是将列表中的元素转换为一个List
- flatten再将整个列表进行扁平化
scala> val a = List("hadoop hive spark flink flume", "kudu hbase sqoop storm")
a: List[String] = List(hadoop hive spark flink flume, kudu hbase sqoop storm)
scala> a.flatMap(_.split(" "))
res7: List[String] = List(hadoop, hive, spark, flink, flume, kudu, hbase, sqoop, storm)
过滤 filter
过滤符合一定条件的元素
scala> List(1,2,3,4,5,6,7,8,9).filter(_ % 2 == 0)
res8: List[Int] = List(2, 4, 6, 8)
排序
- sorted默认排序
scala> List(3,1,2,9,7).sorted
res16: List[Int] = List(1, 2, 3, 7, 9)
- sortBy指定字段排序
scala> val a = List("01 hadoop", "02 flume", "03 hive", "04 spark")
a: List[String] = List(01 hadoop, 02 flume, 03 hive, 04 spark)
// 获取单词字段
scala> a.sortBy(_.split(" ")(1))
res8: List[String] = List(02 flume, 01 hadoop, 03 hive, 04 spark)
- sortWith自定义排序
scala> val a = List(2,3,1,6,4,5)
a: List[Int] = List(2, 3, 1, 6, 4, 5)
scala> a.sortWith((x,y) => if(x<y)true else false)
res15: List[Int] = List(1, 2, 3, 4, 5, 6)
分组 groupBy
groupBy表示按照函数将列表分成不同的组
scala> val a = List("张三"->"男", "李四"->"女", "王五"->"男")
a: List[(String, String)] = List((张三,男), (李四,女), (王五,男))
// 按照性别分组
scala> a.groupBy(_._2)
聚合操作
reduce
reduce表示将列表,传入一个函数进行聚合计算
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> a.reduce((x,y) => x + y)
res5: Int = 55
折叠 fold
fold与reduce很像,但是多了一个指定初始值参数
scala> val a = List(1,2,3,4,5,6,7,8,9,10)
a: List[Int] = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
scala> a.fold(0)(_ + _)
res4: Int = 155
二、面向对象
1、类和对象
创建类和对象
- 使用
class
来定义一个类 - 使用
new
来创建对象 - 如果类是空的,没有任何成员,可以省略
{}
- 如果构造器的参数为空,可以省略
()
object _01ClassDemo {
// 创建类
class Person{}
def main(args: Array[String]): Unit = {
// 创建对象
val p = new Person()
println(p)
}
}
简写:
object _02ClassDemo {
// 创建类,省略花括号
class Person
def main(args: Array[String]): Unit = {
// 创建对象,省略括号
val person = new Person
}
}
2、定义和访问成员变量
- 在类中使用
var/val
来定义成员变量 - 对象直接使用成员变量名称来访问成员变量
object _03ClassDemo {
class Person {
// 定义成员变量
var name = ""
var age = 0
}
def main(args: Array[String]): Unit = {
// 创建Person对象
val person = new Person
person.name = "zhangsan"
person.age = 20
// 获取变量值
println(person.name)
println(person.age)
}
}
3、使用下划线初始化成员变量
- 在定义
var
类型的成员变量时,可以使用_
来初始化成员变量 val
类型的成员变量,必须要自己手动初始化
class Person{
// 使用下划线进行初始化
var name:String = _
var age:Int = _
}
4、定义成员方法
在scala的类中,也是使用def
来定义成员方法
class Customer {
var name:String = _
var sex:String = _
// 定义成员方法
def sayHi(msg:String) = {
println(msg)
}
}
5、访问修饰符
Java中的访问控制,同样适用于scala,可以在成员前面添加private/protected关键字来控制成员的可见性。但在scala中,没有public关键字
,任何没有被标为private或protected的成员都是公共的
6、类的构造器
主构造器
- 主构造器的参数列表是直接定义在类名后面,添加了val/var表示直接通过主构造器定义成员变量
- 构造器参数列表可以指定默认值
- 创建实例,调用构造器可以指定字段进行初始化
- 整个class中除了字段定义和方法定义的代码都是构造代码
class 类名(var/val 参数名:类型 = 默认值, var/val 参数名:类型 = 默认值){
// 构造代码块
}
示例:
// 定义类的主构造器
// 指定默认值
class Person(var name:String = "", var age:Int = 0) {
println("调用主构造器")
}
辅助构造器
- 定义辅助构造器与定义方法一样,也使用
def
关键字来定义 - 这个方法的名字为
this
- 辅助构造器的第一行代码,必须要调用主构造器或者其他辅助构造器
def this(参数名:类型, 参数名:类型) {
// 第一行需要调用主构造器或者其他构造器
// 构造器代码
}
示例:
class Customer(var name:String = "", var address:String = "") {
// 定义辅助构造器
def this(arr:Array[String]) = {
// 辅助构造器必须要调用主构造器或者其他辅助构造器
this(arr(0), arr(1))
}
}
单例对象(Object)
scala中没有Java中的静态成员,我们想要定义类似于Java的static变量、static方法,就要使用到scala中的单例对象——object.
定义单例对象
- 单例对象表示全局仅有一个对象(类似于Java static概念)
- 定义单例对象和定义类很像,就是把class换成object
- 在object中定义的成员变量类似于Java的静态变量
- 可以使用object直接引用成员变量
object _08ObjectDemo {
// 定义一个单例对象
object Dog {
// 定义腿的数量
val LEG_NUM = 4
}
def main(args: Array[String]): Unit = {
println(Dog.LEG_NUM)
}
}
8、main方法
scala和Java一样,如果要运行一个程序,必须有一个main方法。而在Java中main方法是静态的,而在scala中没有静态方法。在scala中,这个main方法必须放在一个单例对象中。
定义
def main(args:Array[String]):Unit = {
// 方法体
}
9、伴生对象
类同时有实例成员又有静态成员
定义
一个class和object具有同样的名字。这个object称为伴生对象,这个class称为伴生类
- 伴生对象必须要和伴生类一样的名字
- 伴生对象和伴生类在同一个scala源文件中
- 伴生对象和伴生类可以互相访问private属性
object _11ObjectDemo {
class CustomerService {
def save() = {
println(s"${CustomerService.SERVICE_NAME}:保存客户")
}
}
// CustomerService的伴生对象
object CustomerService {
private val SERVICE_NAME = "CustomerService"
}
def main(args: Array[String]): Unit = {
val customerService = new CustomerService()
customerService.save()
}
}
10、继承
定义
- scala和Java一样,使用extends关键字来实现继承
- 可以在子类中定义父类中没有的字段和方法,或者重写父类的方法
- 类和单例对象都可以从某个父类继承
class/object 子类 extends 父类 {
..
}
override和super
我们在子类中使用override需要来重写父类的成员,可以使用super来引用父类
- 子类要覆盖父类中的一个方法,必须要使用override关键字
- 使用override来重写一个val字段
- 使用super关键字来访问父类的成员方法
11、类型判断
- isInstanceOf判断对象是否为指定类的对象
- asInstanceOf将对象转换为指定类型
// 判断对象是否为指定类型
val trueOrFalse:Boolean = 对象.isInstanceOf[类型]
// 将对象转换为指定类型
val 变量 = 对象.asInstanceOf[类型]
isInstanceOf 只能判断对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象。如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 。
- p.getClass可以精确获取对象的类型
- classOf[x]可以精确获取类型
- 使用==操作符可以直接比较类型
12、抽象类
定义
如果类的某个成员在当前类中的定义是不包含完整的,它就是一个抽象类
定义抽象类和Java一样,在类前面加上abstract关键字
- 方法没有方法体(抽象方法)
- 变量没有初始化(抽象字段)
// 定义抽象类
abstract class 抽象类名 {
// 定义抽象字段
val 抽象字段名:类型
// 定义抽象方法
def 方法名(参数:参数类型,参数:参数类型...):返回类型
}
13、匿名内部类
匿名内部类是没有名称的子类,直接用来创建实例对象。
val/var 变量名 = new 类/抽象类 {
// 重写方法
}
14、特质(trait)
定义
-
特质是scala中代码复用的基础单元
-
它可以将方法和字段定义封装起来,然后添加到类中
-
与类继承不一样的是,类继承要求每个类都只能继承
一个
超类,而一个类可以添加任意数量
的特质。 -
特质的定义和抽象类的定义很像,但它是使用
trait
关键字 -
使用
extends
来继承trait(scala不论是类还是特质,都是使用extends关键字) -
如果要继承多个trait,则使用
with
关键字 -
和类一样,trait中还可以定义具体的方法
-
在trait中可以定义具体字段和抽象字段
-
继承trait的子类自动拥有trait中定义的字段
-
字段直接被添加到子类中
//定义特质
trait 名称 {
// 抽象字段
// 抽象方法
}
//继承特质
class 类 extends 特质1 with 特质2 {
// 字段实现
// 方法实现
}
对象混入trait
scala中可以将trait混入到对象中,就是将trait中定义的方法、字段添加到一个对象中
val/var 对象名 = new 类 with 特质
trait Logger {
def log(msg:String) = println(msg)
}
class UserService
def main(args: Array[String]): Unit = {
val service = new UserService with Logger
service.log("混入的方法")
}
trait的构造机制
- trait也有构造代码,但和类不一样,特质不能有构造器参数
- 每个特质只有**
一个无参数
**的构造器。 - 一个类继承另一个类、以及多个trait,当创建该类的实例时,它的构造顺序如下:
- 执行父类的构造器
从左到右
依次执行trait的构造器- 如果trait有父trait,先构造父trait,如果多个trait有同样的父trait,则只初始化一次
- 执行子类构造器
trait继承class
trait也可以继承class的。特质会将class中的成员都继承下来。
三、Scala高级
1、样例类
样例类是一种特殊类,它可以用来快速定义一个用于保存数据的类(类似于Java POJO类)
定义样例类
- 如果要实现某个成员变量可以被修改,可以添加var
- 默认为val,可以省略
case class 样例类名([var/val] 成员变量名1:类型1, 成员变量名2:类型2, 成员变量名3:类型3)
样例类的方法
当我们定义一个样例类,编译器自动帮助我们实现了以下几个有用的方法:
- apply方法
//apply方法可以让我们快速地使用类名来创建对象
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
}
}
- toString方法
//toString返回样例类名称(成员变量1, 成员变量2, 成员变量3....),我们可以更方面查看样例类的成员
case class CasePerson(name:String, age:Int)
object CaseClassDemo {
def main(args: Array[String]): Unit = {
val lisi = CasePerson("李四", 21)
println(lisi.toString)
// 输出:CasePerson(李四,21)
}
}
- equals方法
//样例类自动实现了equals方法,可以直接使用==比较两个样例类是否相等,即所有的成员变量是否相等
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 21)
println(lisi1 == lisi2)
// 输出:true
- hashCode方法
//样例类自动实现了hashCode方法,如果所有成员变量的值相同,则hash值相同,只要有一个不一样,则hash值不一样。
val lisi1 = CasePerson("李四", 21)
val lisi2 = CasePerson("李四", 22)
println(lisi1.hashCode())
println(lisi2.hashCode())
- copy方法
//样例类实现了copy方法,可以快速创建一个相同的实例对象,可以使用带名参数指定给成员进行重新赋值
val lisi1 = CasePerson("李四", 21)
val wangwu = lisi1.copy(name="王五")
println(wangwu)
2、样例对象
它主要用在两个地方:
- 定义枚举
- 作为没有任何参数的消息传递
定义:
使用case object可以创建样例对象。样例对象是单例的,而且它没有主构造器
case object 样例对象名
3、模式匹配
scala中有一个非常强大的模式匹配机制,可以应用在很多场景:
- switch语句
- 类型查询
- 使用模式匹配快速获取数据
简单模式匹配
在Java中,有switch关键字,可以简化if条件判断语句。在scala中,可以使用match表达式替代。
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量3" => 表达式3
case _ => 表达式4 // 默认配
}
匹配类型
除了像Java中的switch匹配数据之外,match表达式还可以进行类型匹配。如果我们要根据不同的数据类型,来执行不同的逻辑,也可以使用match表达式来实现。
变量 match {
case 类型1变量名: 类型1 => 表达式1
case 类型2变量名: 类型2 => 表达式2
case 类型3变量名: 类型3 => 表达式3
...
case _ => 表达式4
}
守卫
也就是在case语句中添加if条件判断。
val a = StdIn.readInt()
a match {
case _ if a >= 0 && a <= 3 => println("[0-3]")
case _ if a >= 4 && a <= 8 => println("[3-8]")
case _ => println("未匹配")
}
匹配样例类
匹配集合
匹配数组
匹配列表
匹配元组
4、option类型
使用Option类型,可以用来有效避免空引用(null)异常。
定义
scala中,Option类型来表示可选值。这种类型的数据有两种形式:
- Some(x):表示实际的值
- None:表示没有值
- 使用getOrElse方法,当值为None是可以指定一个默认值
5、偏函数
偏函数可以提供了简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更加优雅。
定义
- 偏函数被包在花括号内没有match的一组case语句是一个偏函数
- 偏函数是PartialFunction[A, B]的一个实例
- A代表输入参数类型
- B代表返回结果类型
6、正则表达式
定义
Regex类
-
scala中提供了Regex类来定义正则表达式
-
要构造一个RegEx对象,直接使用String类的r方法即可
-
建议使用三个双引号来表示正则表达式,不然就得对正则中的反斜杠来进行转义
val regEx = """正则表达式""".r
findAllMatchIn方法
- 使用findAllMatchIn方法可以获取到所有正则匹配到的字符串
7、异常处理
捕获异常
try {
// 代码
}
catch {
case ex:异常类型1 => // 代码
case ex:异常类型2 => // 代码
}
finally {
// 代码
}
抛出异常
def main(args: Array[String]): Unit = {
throw new Exception("这是一个异常")
}
Exception in thread "main" java.lang.Exception: 这是一个异常
at ForDemo$.main(ForDemo.scala:3)
at ForDemo.main(ForDemo.scala)
8、泛型
定义泛型方法
def 方法名[泛型名称](..) = {
//...
}
泛型类
- 定义一个泛型类,直接在类名后面加上方括号,指定要使用的泛型参数
- 指定类对应的泛型参数后,就使用这些类型参数来定义变量了
class 类[T](val 变量名: T)
上下界定义
我们在定义方法/类的泛型时,限定必须从哪个类继承、或者必须是哪个类的父类。此时,就需要使用到上下界。
上界
使用<: 类型名
表示给类型添加一个上界,表示泛型参数必须要从该类(或本身)继承
[T <: 类型]
下界
上界是要求必须是某个类的子类,或者必须从某个类继承,而下界是必须是某个类的父类(或本身)
[T >: 类型]
协变,逆变,非变
协变
- 类型B是A的子类型,Pair[B]可以认为是Pair[A]的子类型
- 参数化类型的方向和类型的方向是一致的。
class Pair[+T]
逆变
- 类型B是A的子类型,Pair[A]反过来可以认为是Pair[B]的子类型
- 参数化类型的方向和类型的方向是相反的
class Pair[-T]
非变
- 默认泛型类是非变的
- 类型B是A的子类型,Pair[A]和Pair[B]没有任何从属关系
- Java是一样的
class Pair[T]{}
9、高阶函数
作为值得函数
在scala中,函数就像和数字、字符串一样,可以将函数传递给一个方法。我们可以对算法进行封装,然后将具体的动作传递给方法,这种特性很有用。
匿名函数
在scala中,可以不需要给函数赋值给变量,没有赋值给变量的函数就是匿名函数
柯里化
柯里化(Currying)是指将原先接受多个参数的方法转换为多个只有一个参数的参数列表的过程。
闭包
闭包其实就是一个函数,只不过这个函数的返回值依赖于声明在函数外部的变量。
10、隐式转换和隐式参数
定义
所谓隐式转换,是指以implicit关键字声明的带有单个参数的方法。它是自动被调用的,自动将某种类型转换为另外一种类型。
- 在object中定义隐式转换方法(使用implicit)
- 在需要用到隐式转换的地方,引入隐式转换(使用import)
- 自动调用隐式转化后的方法
隐式转换的时机
- 当对象调用类中不存在的方法或者成员时,编译器会自动将对象进行隐式转换
- 当方法中的参数的类型与目标类型不一致时
自动导入隐式转换方法
在scala中,如果在当前作用域中有隐式转换方法,会自动导入隐式转换。
隐式参数
方法可以带有一个标记为implicit的参数列表。这种情况,编译器会查找缺省值,提供给该方法。
- 在方法后面添加一个参数列表,参数使用implicit修饰
- 在object中定义implicit修饰的隐式值
- 调用方法,可以不传入implicit修饰的参数列表,编译器会自动查找缺省值
- 和隐式转换一样,可以使用import手动导入隐式参数
- 如果在当前作用域定义了隐式值,会自动进行导入