scala语言是纯粹面向对象的语言,对象化的程度比Java更深,其中有个特质是比较特别的–implicit,用于隐式转换和隐式参数。下面为其介绍。
-
隐式参数:把一些变量声明为隐式参数,把一些方法的参数声明为可以支持隐式参数的,当调用方法时,如果没有传递该参数,那么可以去使用隐式参数。
-
隐式转换:首先提供一些类型转换的能力(通过隐式类和隐式方法),然后在调用某些方法时,当某些对象的类型不匹配,可以使用对应的隐式转换去转换对象的类型。
implicit 使用的一些限制
查找范围
当需要查找隐式对象、隐式方法、隐式类时,查找的范围是:
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都会被搜索
一次规则
编译器在需要使用 implicit 定义时,只会试图转换一次,也就是编译器永远不会把 x + y 改写成 convert1(convert2(x)) + y。
无歧义
对于隐式参数,如果查找范围内有两个该类型的变量,则编译报错。
对于隐式转换,如果查找范围内有两个从A类型到B类型的隐式转换,则编译报错。
隐式参数
在声明方法时,在参数的前面添加implict修饰符,当调用方法时,如果没有传递该参数,那么编译器会去上述的查找范围去查找隐式参数,如果存在,就使用查到的隐式参数作为参数值。可以参考如下的例子:
package net.qingtian.scala.test1.implicit_
/*
隐式参数Test
*/
object ImplicitValueTest {
def showStr(implicit name: String) = println(name)
def main(args: Array[String]): Unit = {
}
// def f1() = {
// // 执行下面一行代码失败,因为没有参数
// showStr
// }
//
// def f2() = {
// implicit val str = "隐式参数"
//
// // 执行下面方法成功,因为能找到适当的隐式参数
// showStr
// }
//
// def f3() = {
// implicit val str1 = "隐式参数1"
// implicit val str2 = "隐式参数2"
//
// // 执行下面一行代码失败,因为有两个隐式参数showStr
// showStr
// }
}
/*
隐式参数的例子,来源于 https://docs.scala-lang.org/zh-cn/tour/implicit-parameters.html
*/
abstract class Monoid[A] {
def add(x: A, y: A): A
def unit: A
}
object ImplicitValueTest2 {
implicit val stringMonoid: Monoid[String] = new Monoid[String] {
def add(x: String, y: String): String = x concat y
def unit: String = ""
}
implicit val intMonoid: Monoid[Int] = new Monoid[Int] {
def add(x: Int, y: Int): Int = x + y
def unit: Int = 0
}
def sum[A](xs: List[A])(implicit m: Monoid[A]): A =
if (xs.isEmpty) m.unit
else m.add(xs.head, sum(xs.tail))
def main(args: Array[String]): Unit = {
println(sum(List(1, 2, 3))) // uses IntMonoid implicitly
println(sum(List("a", "b", "c"))) // uses StringMonoid implicitly
}
}
隐式转换
什么情况下使用隐式转换?
在两种情况下会使用隐式转换:
①调用方法时,传递的参数类型与方法声明的参数类型不同时,编译器会在查找范围下查找隐式转换,把传递的参数转变为方法声明需要的类型。
②调用方法时,如果对象没有该方法,那么编译器会在查找范围内查找隐式转换,把调用方法的对象转变成有该方法的类型的对象。
隐式转换有哪些?
隐式方法和隐式类。
package net.qingtian.scala.test1.implicit_
object ImplicitObject1 {
implicit def doubleToInt(x: Double) = x toInt
}
/*
隐式转换方法参数
使用隐式方法转换
*/
object ImplicitConvertTest1 {
def showInt(i: Int) = println("i is " + i)
def main(args: Array[String]): Unit = {
import net.qingtian.scala.test1.implicit_.ImplicitObject1._
showInt(1.1)
val ii: Int = 1.2
println("ii is " + ii)
}
}
下面代码展示了使用隐式转换转换调用不存在方法的对象:
package net.qingtian.scala.test1.implicit_
class Speaker {
def speak(str: String) = println("Speaker说:" + str)
}
class Aminal
object ImplicitObject2 {
implicit class StringImprovement(val s: String) { //隐式类
def increment = s.map(x => (x + 1).toChar)
}
implicit def toSpeaker(s: Aminal) = new Speaker
}
object ImplicitConvertTest2 {
def main(args: Array[String]): Unit = {
import net.qingtian.scala.test1.implicit_.ImplicitObject2._
// 通过隐式类进行转换
// 把字符串"abcde"隐式转换成ImplicitObject2.StringImprovement
println("abcde".increment)
// 通过隐式方法转换
// 把rabbit对象转换成Speaker对象使用
val rabbit = new Aminal
rabbit.speak("hello world")
}
}
我们构造Map对象时,使用的 -> 符号就是使用了隐式转换。
Map(1 -> "One", 2->"Two",3->"Three")
// Map中的值被隐式转换成ArrowAssoc对象,ArrowAssoc对象的 -> 方法生成元组。 -> 方法的实现如下:
def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
转载自:https://blog.csdn.net/bingospunky/article/details/88854352