目录
隐式转换的目的:
通过隐式转换,程序员可以在编写Scala程序时故意漏掉一些信息,让编译器去尝试在编译期间自动推导出这些信息来,这种特性可以极大的减少代码量,忽略那些冗长,过于细节的代码。
隐式转换的时机:
(1)当方法中的参数的类型与目标类型不一致时
(2)当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(根据类型)
一、隐式函数
implicit def 函数名(参数:参数类型):返回值类型={
函数体
返回值
}
当main方法中,某个变量类型A => 返回值类型B,编译器会自动寻找隐式函数(参数类型A、返回值类型B)进行转换,并且返回值是通过隐式函数函数体计算得出结果
(1)编译器只关注隐式函数的函数签名(参数类型和返回值类型),与函数名无关
(2)隐式函数只能有一个参数
(3)同一个作用域内不能存在两个相同函数签名的隐式函数,否则会报错,因为表达式不知道调用哪个隐式函数
实例1:隐式函数类型转换
· e是一个s类型的表达式,需要的是T类型,编译器就会寻找S => T的隐式函数,自动转换
object ImplicitDemo1 {
def main(args: Array[String]): Unit = {
//隐式函数
implicit def imp(d:Double):Int={
d.toInt
}
var a:Int = 3.9
println(a)
}
}
---------
3
变量a,传入值是Double类型,返回Int类型;自动寻找隐式函数imp,调用函数体 3.9.toInt,最后返回Int类型的a是3;打印结果3
实例2:隐式函数 & 类结合使用
· e是一个S类型的表达式,使用点号访问e.m时,m不是类型S的成员而类A的成员,那么编译器会寻找隐式函数(S => A),自动转换,使e.m合法
//要求 x days ago,返回x天前的日期;
//x days future,返回x天后的日期
object ImplicitDemo3 {
def main(args: Array[String]): Unit = {
implicit def int2DateHelper(num: Int): DateHelper = new DateHelper(num)
println(2 days "ago")
println(3 days "future")
}
}
class DateHelper(var num: Int) {
var today = LocalDate.now()
def days(when: String): LocalDate = {
if (when == "ago") today.minusDays(num)
else today.plusDays(num)
}
}
---------------
2019-01-07
2019-01-15
思路:
写一个days方法,x(Int),x.days("ago")时,计算当前时间x天前的日期;x.days("future")时,计算当天时间x天后的日期
x是基本数据类型Int,不能直接调用方法;所以需要把Int => 类,类中有days方法
所以需要1. 写一个类,里面有days方法; 2. 隐式函数,把Int(参数类型) => 类(返回值类型)。
二、隐式变量(隐式值)& 隐式参数
(1)隐式变量
implicit 修饰符 参数名:参数类型 = 默认值
(2)隐式参数
def 函数名(implicit 参数名:参数类型):返回值类型 = {
函数体
返回值
}
函数中给参数加上implicit,表明这是隐式参数,在调用该函数时,可以不用传递隐式参数,直接用函数名,
编译器会寻找一个被implicit标记过的隐式变量,隐式变量的参数类型,与隐式参数的类型一致,可以直接调用隐式变量作为隐式参数
object ImplicitDemo4 {
def main(args: Array[String]): Unit = {
say("kevin")
say
}
//隐式参数
def say(implicit name: String) = {
val str: String = name + "你好..."
println(str)
}
//隐式变量
implicit var name1: String = "name1"
}
--------------
kevin你好...
name1你好...
可见,隐式变量自动引入隐式参数时,与变量名无关,只匹配变量类型;当函数没有参数时,直接调用同类型的隐式变量作为隐式参数传入def say函数进行运算。
ps:隐式值的优先级高于默认值
三、隐式类
隐式类用于扩展已存在的类;用法等价于之前的隐式函数和类结合使用,写法更简洁
-> 隐式函数 & 类结合使用:
先构造类写方法体;再创建隐式函数,返回值就是new 类对象,隐式函数与返回值的形参列表一致;再用隐式函数去转换
-> 隐式类:创建隐式类,再用隐式类去new对象;直接转换。
核心:符合隐式类形参列表中参数类型的变量,都可以调用隐式类中的方法
implicit class 类名(修饰符 参数:参数类型){
//类主体
}
隐式类的特点:
(1)构造参数有且只有一个
(2)隐式类必须定义在类、伴生对象、包对象里,即隐式类不能是顶级的
(3)作用域内不能有与之相同名称的标识符
(4)implicit关键词不能用于case类
实例:与 隐式函数 & 类结合使用的对比
//要求 x days ago,返回x天前的日期;
//x days future,返回x天后的日期
object ImplicitClass1 {
implicit class DateHelper(val num : Int){
val today = LocalDate.now()
def days(when:String)={
if(when == "ago") today.minusDays(num)
else today.plusDays(num)
}
}
def main(args: Array[String]): Unit = {
println(2 days "ago")
println(4 days "future")
}
}
---------------
2019-01-08
2019-01-14
与一种隐式函数 & 类的写法对比,简化了创建隐式函数的步骤,代码更简洁
四、隐式转换机制
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
1 .首先会在当前代码作用域下查找隐式实体(隐式方法 隐式类 隐式对象)
2. 如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下:(1)如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索
(2)如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的
伴生对象和String的伴生对象(3) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索
(4) 如果T是个类型注入S#T,那么S和T都会被搜索