Scala implicit关键字

  Scala的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.scala.test1.implicit_

/*
隐式参数Test
*/
object ImplicitValueTest {
  def showStr(implicit name: String) = println(name)
  def main(args: Array[String]): Unit = {
  }
}

/*
隐式参数的例子,来源于 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.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.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.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)
发布了162 篇原创文章 · 获赞 58 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/ThreeAspects/article/details/105405015
今日推荐