Intro
在实际工作中,可能需要指定某些数据类型的相互转化,这时候会用到隐式转换和隐式函数。看一个Demo:
class test(){
implicit def f1(d: Double): Int = {
d.toInt
}
var x:Int=3.9
}
val testObj = new test
testObj.x
warning: there was one feature warning; re-run with -feature for details
defined class test
testObj: test = test@799609b4
res22: Int = 3
此时test类中的属性x被强制从Double转换成Int
隐式转换的细节
- 隐式转换函数的函数名可以是任意的,隐式转换与函数名称无关,只与函数签名(函数参数类型和返回值类型)有关
- 隐式函数可以有多个(即:隐式函数列表),但是需要保证在当前环境下,只有一个隐式函数能被识别,也就是说都是Double2Int的隐式函数不能有多个
隐式转换的作用
如果仅仅用来做数据转换,似乎还不大材小用。如果需要为一个类增加一个方法,也可通过隐式转换来实现(动态增加功能)。比如想为MySQL类增加一个delete方法。这个有点像动态混入trait,不用修改类的源代码,直接增加方法。看个Demo:
class MySQL{
def insert(): Unit = {
println("MySQL:insert...")
}
}
class DB {
def delete1(): Unit = {
println("DB:delete1...")
}
}
implicit def addDelete(mysql:MySQL): DB = {
new DB //这里好像有点类似继承了DB一样。。。
}
trait addDelete2{
def delete2(): Unit = {
println("trait:delete2...")
}
}
val mysql = new MySQL with addDelete2
mysql.insert()
mysql.delete1()
mysql.delete2()
MySQL:insert...
DB:delete1...
trait:delete2...
warning: there was one feature warning; re-run with -feature for details
defined class MySQL
defined class DB
addDelete: (mysql: MySQL)DB
defined trait addDelete2
mysql: MySQL with addDelete2 = $anon$1@13da528e
如上所示,特质和隐私函数似乎都给MySQL类增加了delete方法,这俩有啥差别吗?我也母鸡。。。
隐式值
隐式值也叫隐式变量,将某个形参变量标记为implicit,所以编译器会在方法省略隐式参数的情况下去搜索作用域内的隐式值作为缺省参数。还是看个Demo:
// 隐式变量
implicit val str1: String = "jack"
//隐式参数
def hello(implicit name: String): Unit = {
println(name + " hello")
}
def sayHi(implicit name: String ="wade")
: Unit = {
println(name + " hi")
}
hello //注意:调用.不带()
sayHi
jack hello
jack hi
str1: String = jack
hello: (implicit name: String)Unit
sayHi: (implicit name: String)Unit
细节
- 隐式变量不能有重复的,比如两个隐式变量都是指定String的值
- 当时同时有隐式参数默认值和隐式变量时,隐式变量优先级更高,见sayHi
- 如果有隐式参数但是没有对应的隐式变量,则报错
隐式类
之前我们通过隐式函数,给mysql增加了delete方法,隐式类也可以,看个demo:
class MySQL1 {
def sayOk(): Unit = {
println("sayOk")
}
}
//DB1会对应生成隐式类
implicit class DB1(val m: MySQL1) {
def addSuffix(): Unit = {
println( "DB1addSuffix ... ")
}
}
val mysql1 = new MySQL1
mysql1.sayOk()
// 此时mysql1有addSuffix方法
mysql1.addSuffix()
sayOk
DB1addSuffix ...
defined class MySQL1
defined class DB1
mysql1: MySQL1 = MySQL1@2b2b690d
隐式类的细节
- 其所带的构造参数有且只能有一个
- 隐式类必须被定义在“类”或“伴生对象”或“包对象”里,即隐式类不能是 顶级的(top-level objects)
- 隐式类不能是case class
- 作用域内不能有与之相同名称的标识符,比如不能有其他DB1
隐式转换其他细节
隐式准换的时机
- 当方法中的参数的类型与目标类型不一致时,比如前面数据类型转换
- 当对象调用所在类中不存在的方法或成员时,编译器会自动将对象进行隐式转换(根据类型)
隐式转换的前提
- 不能存在二义性,比如功能一样的隐式函数
- 隐式操作不能嵌套使用,比如在隐式函数toInt内部做赋值操作
var x:Int=3.9
2020-03-09 于南京市栖霞区