Implicits in Scala refers to either a value that can be passed “automatically”, so to speak, or a conversion from one type to another that is made automatically.
大意是讲implicit
在scala中是指一个可以被自动转换的值,也就是说类型的转换是自动完成的。
隐式类型转换
譬如在方法入参限定为String
类型的情况下,在调用该方法之前,通过声明隐式类型转换,就可以使该方法达到“重载”的效果:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15object TestImplicit {
def display(words: String): Unit = println(words.getClass + ": " + words)
implicit def convert2String(value: Boolean): String = if (value) "true" else "false"
implicit def convert2String(value: Int): String = value.toString
def main(args: Array[String]): Unit = {
display("learning scala.")
display(2)
display(true)
// display(2.3) // 报错,没有Double转String的声明和display(value: Double)的重载
}
}
其运行结果为:1
2
3class java.lang.String: learning scala.
class java.lang.String: 2
class java.lang.String: true
注意
隐式转换的声明要放置在需要使用隐式转换被调用之前,否则达不到隐式转换的效果,也就会报错。还有,当display
这个方法有重载,使其传入的参数为Boolean
类型,则scala会自动使用以声明的重载去完成方法的调用。
1 def display(value: Boolean): Unit = {if(value) println("real true") else println("real false")}
其运行结果为:
1
2
3 class java.lang.String: learning scala.
class java.lang.String: 2
real true
scala能智能的选择在何时使用隐式转换:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 object TestImplicit {
def display(words: String): Unit = println(words.getClass + ": " + words)
def display(value: Boolean): Unit = {if(value) println("real true") else println("real false")}
implicit def convert2String(value: Boolean): String = if (value) "true" else "false"
implicit def convert2String(value: Int): String = value.toString
def displayInt1(value: Int): Unit = println(value.getClass + ": " + value + 12)
def displayInt2(value: Int): Unit = println(value.getClass + ": " + value + "heheda")
def main(args: Array[String]): Unit = {
display("learning scala.")
display(2)
display(true)
// 在此时虽然已经声明了Int对String的隐式转换,但以下都不会进行隐式转换
displayInt1(12)
displayInt2(12)
}
}
其运行结果为:
1
2
3
4
5 class java.lang.String: learning scala.
class java.lang.String: 2
real true
int: 1212
int: 12heheda
隐式类型声明
隐式指定方法中参数的类型:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23object TestImplicit {
/**
* 声明foo的参数应为数值类型(BigInt,Long,Int,Char,Short,Byte都是Integral类型,可参见scala.math.Numeric)
*/
def foo[T](t: T)(implicit integral: Integral[T]): Unit = {
println(integral)
println(t.getClass + "\n--------------------------")
}
def main(args: Array[String]): Unit = {
foo(123)
val c: Char = 'a'
foo(c)
val b: Byte = 23
foo(b)
val s: Short = 3
foo(s)
// 以下代码报错
// val d: Double = 2.3
// foo(d)
// val str: String = "string"
// foo(str)
}
}
运行结果:1
2
3
4
5
6
7
8
9
10
11
12scala.math.Numeric$IntIsIntegral$@56cbfb61
class java.lang.Integer
--------------------------
scala.math.Numeric$CharIsIntegral$@6bdf28bb
class java.lang.Character
--------------------------
scala.math.Numeric$ByteIsIntegral$@27f674d
class java.lang.Byte
--------------------------
scala.math.Numeric$ShortIsIntegral$@43a25848
class java.lang.Short
--------------------------
隐式声明默认值
在方法参数列表中,对参数使用implicit
声明,则在不传该参数的时候,则该参数会使用隐式声明的值:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19object TestImplicit {
def main(args: Array[String]): Unit = {
printDefault("hello")("world")
// 导入隐式声明
import DefaultValueImplicitContext._
printDefault("hello") // 第二个参数未赋值,则使用默认值
}
object DefaultValueImplicitContext {
implicit val defaultString: String = "WORLD"
}
/**
* 柯里化,使用implicit
*/
def printDefault(prefix: String)(implicit suffix: String): Unit = {
println(prefix + " " + suffix)
}
}
运行结果为:1
2hello world
hello WORLD
扩展类的功能
还可以通过隐式声明,通过非继承的方式扩展类的功能:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27object TestImplicit {
/**
* 声明只有一个功能(shot)的类
* @param bullet 子弹数量
*/
class Gun(val bullet: Int) {
def shot(): Unit = {
printf("Gun can shot %d times. \n", bullet)
}
}
/**
* 检查子弹的数量
* 隐式转换不是通过继承拓展类功能,而是将类作为隐式转换类的参数,传入,并将其功能进行增强
* @param gun
*/
implicit class GunExt(gun: Gun) {
def check(): Unit = {
printf("Gun has %d bullets. \n", gun.bullet)
}
}
def main(args: Array[String]):Unit = {
val gun: Gun = new Gun(6)
gun.shot()
gun.check() // 通过增强的方法
}
}
运行结果:1
2Gun can shot 6 times.
Gun has 6 bullets.
注意
- 使用
implicit
跟声明时候的名称没有关系- 声明隐式默认值时,同一种数据类型只能声明一个,以下使用是会报错的:
1
2
3
4 object DefaultValueImplicitContext {
implicit val defaultString: String = "WORLD"
implicit val defaultString2: String = "WORLD2"
}
- 一个方法只会有一个隐式参数列表,且置于方法的最后一个参数列表(柯里化)。