隐式转换函数(implicit conversion function)是以implicit关键字声明的带有单个参数的函数,这样的函数将被自动应用,将值从一种类型转换为另一种类型。隐式转换函数叫什么名字是无所谓的,因为通常不会由用户手动调用,而是由Scala进行调用。但是如果要使用隐式转换,则需要对隐式转换函数进行导入。因此通常建议将隐式转换函数的名称命名为“one2one”的形式。
scala会考虑如下位置的隐式转换函数:
1、位于源或目标类型的伴生对象中的隐式函数
2、位于当前作用域可以以单个标识符指代的隐式函数
隐式转换在如下三种不同情况下会被考虑:
1、当表达式类型与预期类型不同时
2、当对象访问一个不存在成员时
3、当对象调用某个方法,而这个方法的参数声明与传入参数不匹配时
有三种情况编译器不会尝试使用隐式转换
1、如果代码能够在不使用隐式转换的前提下通过编译,则不会使用隐式转换
2、编译器不会尝试同事执行多个转换
3、存在二义性的转换是错误。
object Demo1Main {
def mainargs: Array[String]): Unit = {
val num:Int = f13.5);
val num1:Int = 3.5;
printnum)
}
implicit def f1d: Double): Int = {
d.toInt}
}
隐式转换的注意事项和细节
1、隐式转换函数的函数名可以是任意的,隐式转换与函数名称无关,只与函数签名(函数参数类型和返回值类型)有关。
2、隐式函数可以有多个即:隐式函数列表),但是需要保证在当前环境下,只有一个隐式函数能被识别
隐式参数
定义
1、定义一个普通变量,使用 implicit 关键字修饰,定义一个函数调用这个变量当参数时,此时这个参数就被称为隐式参数
2、隐式参数的作用:减少提供函数参数的数量,让某些参数拥有隐藏的值(隐式变量)
def mainargs: Array[String]): Unit = {
//隐式参数每种类型只能有一个)
implicit var a:Int = 10
implicit var str:String = "hello"
def funa:Int,b:Int)implicit c:Int=5):Int={
a+b+c
}
def fun2a:Int,b:Int)c:Int):Int={
a+b+c
}
def fun3a:Int,b:Int)implicit c:Int=5,str:String):Int={
printlnstr)
a+b+c
}
//优先级:传参 > 隐式参数 > 默认
println"fun结果:"+fun10,10)) //30 调用隐式参数10 //25 默认赋值5
println"fun结果:"+fun10,10)20)) //40 传参20
println"fun2结果:"+fun210,10)20))
println"fun3结果:"+fun310,10))
println"fun3结果:"+fun310,10)20,"heheda"))
}
隐式类
基本介绍
在scala2.10后提供了隐式类,可以使用implicit声明类,隐式类的非常强大,同样可以扩展类的功能,比前面使用隐式转换丰富类库功能更加的方便,在集合中隐式类会发挥重要的作用。
隐式类使用有如下几个特点:
其所带的构造参数有且只能有一个
隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是 顶级的top-level objects)。
隐式类不能是case class(case class在后续介绍 样例类)
作用域内不能有与之相同名称的标识符
package com.liu.yinshi
object Demo2Main {
def mainargs: Array[String]): Unit = {
implicit class DB1val mysql: Mysql) {
def addSufix): String = {
mysql.driver + " scala"
}
}
val mysql = new Mysql;
val str = mysql.addSufix)
printlnstr)
}
}
package com.liu.yinshi
class Mysql {
val driver = "mysqlDriver"
def insert): Unit ={
print"insert function")
}
def sayOk): Unit ={
println"sayOk")
}
}
转换时机
当方法中的参数的类型与目标类型不一致时
当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(根据类型)
隐式解析机制
即编译器是如何查找到缺失信息的,解析具有以下两种规则:
首先会在当前代码作用域下查找隐式实体(隐式方法、隐式类、隐式对象)。一般是这种情况)
如果第一条规则查找隐式实体失败,会继续在隐式参数的类型的作用域里查找。类型的作用域是指与该类型相关联的全部伴生模块,一个隐式实体的类型T它的查找范围如下第二种情况范围广且复杂在使用时,应当尽量避免出现):
a) 如果T被定义为T with A with B with C,那么A,B,C都是T的部分,在T的隐式解析过程中,它们的伴生对象都会被搜索。
b) 如果T是参数化类型,那么类型参数和与类型参数相关联的部分都算作T的部分,比如List[String]的隐式搜索会搜索List的伴生对象和String的伴生对象。
c) 如果T是一个单例类型p.T,即T是属于某个p对象内,那么这个p对象也会被搜索。
d) 如果T是个类型注入S#T,那么S和T都会被搜索。
隐式转换的前提
在进行隐式转换时,需要遵守两个基本的前提:
不能存在二义性
隐式操作不能嵌套使用 // [举例:]如:隐式转换函数