/*
* 1. 什么是隐式转换?
* 1. 当编译器第一次编译代码失败时
* 会自动在当前环境中查找能使代码通过编译的方法(通常是数据类型转换)
* 也可以称之为 二次编译
*
* 2. 隐式转换发生在什么时候?
* 1. 发生在编译时期,编译器会自动帮助我们做一些事情
*
* 3. 隐式转换的修饰的位置
* 1. 隐式函数
* 语法 : implicit def f1(参数列表) ...
*
* 2. 隐式类
* 语法 : implicit class objName ...
*
* 要求 : 1. 所带的构造参数 有且只能有一个
* 2. 隐式类必须定义在 类或伴生对象或包对象中
* 即隐式类不能作为 顶级对象
* implicit' modifier cannot be used for top-level objects
*
* 3. 隐式参数
* 语法 : implicit val id:Int = 10 -- 隐式变量
* def f1(implicit str:Int) -- 隐式参数
*
* 4. 隐式解析机制(Scala语法)
* 1. 首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象) (一般是这种情况)
* 2. 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。
* 类型的作用域是指 与该类型相关联的全部伴生对象以及该类型所在包的包对象
* */
-- 1.隐式函数
class MyInt(val self: Int) {
println("MyInt 对象初始化了")
def Max(i: Int): Int = if (self >= i) self else i
def Min(i: Int): Int = if (self >= i) i else self
}
object ImplicitFunction extends App {
// 1.需求 : Int类型的对象,定义比较大小的方法,并返回最大值
// 13.Max(20) 返回 20
// 2.思路 : 定义 Int类型的包装类 MyInt,定义Max方法
// 通过调用 MyInt 来完成需求
println(s"获取max:${new MyInt(13).Max(20)}")
println(s"获取max:${new MyInt(21).Max(20)}")
// 3. 思考 : 每次 完成13.Max(20) 都需要创建 MyInt对象,有没有更简洁的办法呢?
// 可以定义一个 隐式的转换函数,在每次编译的时候,自动将Int对象 转换成MyInt对象
implicit def Int2MyInt(i: Int): MyInt = {
println("隐式函数-Int2MyInt-调用了")
new MyInt(i)
}
println(s"获取max:${13.Max(20)}")
// 隐式调用 会将13作为参数 去当前作用域下去寻找是否有一个函数 参数类型为Int 返回值类型为MyInt
// 如果找到了 将会自动调用 将13.Max(20) => 隐式函数(13).Max(20)
// 4. 思考
// 1. 隐式函数的编译流程 是怎样的?
// 1. 在编译代码时,如果发生编译类型错误,不会立即报错
// 而是根据 参数类型和返回值类型 去当前代码的作用域下
// 寻找有没有 将类型转换的函数,如果找到 则会自动调用
// 2. 注意事项
// 1. 同一作用域下 同一类型(参数类型 + 返回值类型 相同)的隐式函数只能有一个
}
-- 2.隐式类
object ImplicitClass extends App {
//1. 思考 : 在上述需求中,通过隐式函数 完成了 13.Max(20)
// 但是 一旦离开该作用域(比如其他对象或类中),则需要重新 定义隐式转换函数
// 有没有方便的方法,不用定义隐式转换函数,就能完成自动类型转换呢?
//2. 思路 : 可以定义一个 隐式类,通过类的构造器,完成自动类型转换
implicit class MyInt(val self: Int) {
println("MyInt 对象初始化了")
def Max(i: Int): Int = if (self >= i) self else i
def Min(i: Int): Int = if (self >= i) i else self
}
println(s"获取max:${13.Max(20)}") // 隐式调用
//3. 注意事项
// 1. 隐式类所带的构造器,有且只能有一个参数
// 2. 隐式类 必须被定义在 "类"或者"伴生对象"或者"包对象"里
// 也就是隐式类不能作为顶级类
// implicit' modifier cannot be used for top-level objects
}
-- 3.隐式参数
object ImplicitPara extends App {
//1. 思考 : 调用方法时,不指定参数时,也想有参数传递
//2. 思路 :
// 1. 可以使用 默认参数(弊端 :只能在函数定义时,指定参数默认值)
// 2. 可以使用 隐式参数(可以动态指定实参的值)
//定义 隐式变量
implicit var str: String = "大王"
implicit var str1: Int = 100
//隐式参数
def f1(implicit str: String): Unit = println(s"f1方法:${str}")
def f2(implicit str: String = "小王"): Unit = println(s"f2方法:${str}")
//方法体内调用 隐式变量
def f3 = println(s"f2方法:${implicitly[Int]}")
//调用
f1
f1("new")
f2
f2("new")
f3
//3. 说明
// 1. 调用流程
// 通过参数类型 去匹配同作用域下的同类型 隐式变量
// 2. 注意事项
// 同作用域下 同类的隐式变量只能定义一个
// 当定义多个 同类型的隐式变量时, 将造成无法匹配
// 3. 隐式参数和 默认参数 的优先级 谁高呢?
// 隐式参数 > 默认参数
}
//隐式解析机制(测试作用域)
/*
* 1. 首先在 当前作用域下查找 符合条件的 隐式变量
* 2. 当前作用域如果没找到
* 会继续在隐式参数的类型的作用域里查找
* 类型的作用域是指 : 与该类型相关联的全部伴生模块
* */
object ImplicitScope extends App with PersonTrait {
//1. 首先会在当前代码作用域下查找隐式实体
val teacher = new Teacher()
teacher.eat()
teacher.say() //会继续在隐式参数的类型的作用域里查找
class Teacher {
def eat(): Unit = println("eat...")
}
}
trait PersonTrait {
}
object PersonTrait {
// 隐式类 : 类型 1 => 类型 2
implicit class Person5(user: Teacher) {
def say(): Unit = println("say...")
}
}