• Scala implicit


    Scala implicit

    implicit基本含义

    在Scala中有一个关键字是implicit, 之前一直不知道这个货是干什么的,今天整理了一下。

    我们先来看一个例子:

    def display(input:String):Unit = println(input)
    

    我们可以看到,display函数的定义只是接受String类型的入参,因此调用display("any string")这样的函数是没问题的。但是如果调用display(1)这样的入参不是String类型的话,编译会出错的。

    如果我们想让display函数也能够支持Int类型的入参的话,除了我们重新定一个def display(input:Int):Unit = println(input)这样的函数以外,我们还可以在相同的作用域内用implicit关键字定义一个隐式转换函数,示例代码如下:

    object ImplicitDemo {
    
      def display(input:String):Unit = println(input)
    
      implicit def typeConvertor(input:Int):String = input.toString
    
      implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"
    
    //  implicit def booleanTypeConvertor(input:Boolean):String = if(input) "true" else "false"
    
    
      def main(args: Array[String]): Unit = {
        display("1212")
        display(12)
        display(true)
      }
    
    }
    

    我们定义了2个隐式转换函数:

      implicit def typeConvertor(input:Int):String = input.toString
    
      implicit def typeConvertor(input:Boolean):String = if(input) "true" else "false"
    

    这样display函数就可以接受String、Int、Boolean类型的入参了。注意到上面我们的例子中注释的那一行,如果去掉注释的那一行的话,会在运行的时候出现二义性:

    Error:(18, 13) type mismatch;
     found   : Boolean(true)
     required: String
    Note that implicit conversions are not applicable because they are ambiguous:
     both method typeConvertor in object ImplicitDemo of type (input: Boolean)String
     and method booleanTypeConvertor in object ImplicitDemo of type (input: Boolean)String
     are possible conversion functions from Boolean(true) to String
        display(true)
                ^
    

    得出的结论是:

    隐式转换函数是指在同一个作用域下面,一个给定输入类型并自动转换为指定返回类型的函数,这个函数和函数名字无关,和入参名字无关,只和入参类型以及返回类型有关。注意是同一个作用域。

    implicit的应用

    我们可以随便的打开scala函数的一些内置定义,比如我们最常用的map函数中->符号,看起来很像php等语言。
    但实际上->确实是一个ArrowAssoc类的方法,它位于scala源码中的Predef.scala中。下面是这个类的定义:

      final class ArrowAssoc[A](val __leftOfArrow: A) extends AnyVal {
        // `__leftOfArrow` must be a public val to allow inlining. The val
        // used to be called `x`, but now goes by `__leftOfArrow`, as that
        // reduces the chances of a user's writing `foo.__leftOfArrow` and
        // being confused why they get an ambiguous implicit conversion
        // error. (`foo.x` used to produce this error since both
        // any2Ensuring and any2ArrowAssoc pimped an `x` onto everything)
        @deprecated("Use `__leftOfArrow` instead", "2.10.0")
        def x = __leftOfArrow
    
        @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(__leftOfArrow, y)
        def →[B](y: B): Tuple2[A, B] = ->(y)
      }
      @inline implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x)
    

    我们看到def ->[B] (y :B)返回的其实是一个Tuple2[A,B]类型。
    我们定义一个Map:

    scala> val mp = Map(1->"game1",2->"game_2")
    mp: scala.collection.immutable.Map[Int,String] = Map(1 -> game1, 2 -> game_2)
    

    这里 1->"game1"其实是1.->("game_1")的简写。
    这里怎么能让整数类型1能有->方法呢。
    这里其实any2ArrowAssoc隐式函数起作用了,这里接受的参数[A]是泛型的,所以int也不例外。
    调用的是:将整型的1 implicit转换为 ArrowAssoc(1)
    看下构造方法,将1当作__leftOfArrow传入。
    ->方法的真正实现是生产一个Tuple2类型的对象(__leftOfArrow,y ) 等价于(1, "game_id")
    这就是一个典型的隐式转换应用。

    其它还有很多类似的隐式转换,都在Predef.scala中:
    例如:Int,Long,Double都是AnyVal的子类,这三个类型之间没有继承的关系,不能直接相互转换。
    在Java里,我们声明Long的时候要在末尾加上一个L,来声明它是long。
    但在scala里,我们不需要考虑那么多,只需要:

    scala> val l:Long = 10
    l: Long = 10
    

    这就是implicit函数做到的,这也是scala类型推断的一部分,灵活,简洁。
    其实这里调用是:

    val l : Long = int2long(10)
    

    更牛逼的功能

    为现有的类库增加功能的一种方式,用java的话,只能用工具类或者继承的方式来实现,而在scala则还可以采用隐式转化的方式来实现。

    隐式参数

    看一个例子再说:

    object ImplictDemo {
    
      object Context{
        implicit val ccc:String = "implicit"
      }
    
    
      object Param{
        def print(content:String)(implicit prefix:String){
          println(prefix+":"+content)
        }
      }
    
      def main(args: Array[String]) {
        Param.print("jack")("hello")
    
        import Context._
        Param.print("jack")
      }
    
    }
    

    程序运行结果为:

    hello:jack
    implicit:jack
    

    隐式转换扩展

    import java.io.File
    
    import scala.io.Source
    
    class RichFile(val file:File){
      def read = Source.fromFile(file.getPath()).mkString
    }
    
    object Context{
      implicit def file2RichFile(f:File)= new RichFile(f)
    }
    
    object ImplictDemo {
    
      def main(args: Array[String]) {
        import Context.file2RichFile
        println(new File("f:\create.sql").read)
      }
    
    }
    

    上面的代码中调用的read方法其实就是RichFile中定义的read方法。

    最后的总结:

    1. 记住隐式转换函数的同一个scop中不能存在参数和返回值完全相同的2个implicit函数。
    2. 隐式转换函数只在意 输入类型,返回类型。
    3. 隐式转换是scala的语法灵活和简洁的重要组成部分

    参考资料

  • 相关阅读:
    冲刺成果演示
    c#输入串字符不符
    课堂测试—数据清洗
    mapreduce实验
    问题纠错
    软件需求阅读笔记第一篇
    暑假第五周总结
    暑假第四周总结
    暑假第三周总结
    暑假第二周总结
  • 原文地址:https://www.cnblogs.com/rollenholt/p/4118567.html
Copyright © 2020-2023  润新知