zoukankan      html  css  js  c++  java
  • Scala入门到精通——第十九节 隐式转换与隐式參数(二)

    作者:摇摆少年梦
    配套视频地址:http://www.xuetuwuyou.com/course/12

    本节主要内容

    1. 隐式參数中的隐式转换
    2. 函数中隐式參数使用概要
    3. 隐式转换问题梳理

    1. 隐式參数中的隐式转换

    前一讲中,我们提到函数中假设存在隐式參数,在使用该函数的时候假设不给定相应的參数,则编译器会自己主动帮我们搜索相应的隐式值,并将该隐式值作为函数的參数,这里面事实上没有涉及到隐式转换。本节将演示怎样利用隐式參数进行隐式转换。以下的代码给定的是一个普通的比較函数:

    object ImplicitParameter extends App {
    //以下的代码不能编译通过
    //这里面泛型T没有详细指定,它不能直接使用
    //<符号进行比較
      def compare[T](first:T,second:T)={
        if (first < second) 
          first 
        else 
          second
      }
    }

    上面的代码要想使其编译通过,能够前类型变量界定和视图界定指定其上界为Ordered[T],比如:

    object ImplicitParameter extends App {
      //指定T的上界为Ordered[T],全部混入了特质Ordered
      //的类都能够直接的使用<比較符号进行比較
      def compare[T<:Ordered[T]](first:T,second:T)={
        if (first < second) 
          first 
        else 
          second
      }
    }

    这是一种解决方式。我们另一种解决方式就是通过隐式參数的隐式转换来实现。代码例如以下:

    object ImplicitParameter extends App {
    //以下代码中的(implicit order:T=>Ordered[T])
    //给函数compare指定了一个隐式參数
    //该隐式參数是一个隐式转换
      def compare[T](first:T,second:T)(implicit order:T=>Ordered[T])={
        if (first > second) 
          first 
        else 
          second
      }
      println(compare("A","B"))
    }

    2. 函数中隐式參数使用概要

    要点1:在定义函数时,假设函数没有柯里化,implicit关键字会作用于全部參数,比如:

    //implicit关键字在以下的函数中仅仅能出现一次
    //它作用于两个參数x,y,也即x,y都是隐式參数
    def sum(implicit x: Int, y: Int) = x + y
    //以下的函数不合法,函数假设没有柯里化,不能期望
    //implicit关键字会作用于当中一个參数
    //def sum(x: Int, implicit y: Int) = x + y
    //def sum(implicit x: Int, implicit y: Int) = x + y

    另外,值得注意的是。def maxFunc(implicit x: Int, y: Int) = x + y 在使用时。也仅仅能指定一个隐式值。即指定的隐式值分别会相应函数中的參数(这里是x,y)。代码例如以下:

    def sum(implicit x: Int, y: Int) = x + y
    //仅仅能指定一个隐式值
    //比如以下下定义的x会自己主动相应maxFunc中的
    //參数x,y即x=3,y=3,从而得到的结果是6
     implicit val x:Int=3
    //不能定义两个隐式值
    //implicit val y:Int=4
      println(sum)

    要点2:要想使用implicit仅仅作用于某个函数參数,则需要将函数进行柯里化,如:

    def sum(x: Int)(implicit y:Int)=x+y

    值得注意的是,以下这样的两种带隐式參数的函数也是不合法的

    def sum(x: Int)(implicit y:Int)(d:Int)=x+y+d
    def sum(x: Int)(implicit y:Int)(implicit d:Int)=x+y+d

    要点3: 匿名函数不能使用隐式參数。比如:

     val sum2=(implicit x:Int)=>x+1

    要点4: 怎样函数带有隐式參数,则不能使用其偏函数,比如:

    
     def sum(x: Int)(implicit y:Int)=x+y
     //不能定义sum的偏函数。由于它带有隐式參数
     //could not find implicit value for 
    //parameter y: Int
    //not enough arguments for method sum:
    // (implicit y: Int)Int. Unspecified value parameter y.
     def sum2=sum _

    3. 隐式转换问题梳理

    1 多次隐式转换问题
    在上一讲中我们提到。隐式转换从源类型到目标类型不会多次进行。也即源类型到目标类型的转换仅仅会进行一次

    class RichFile(val file:File){
      def read=Source.fromFile(file).getLines().mkString
    }
    
    //RichFileAnother类。里面定义了read2方法
    class RichFileAnother(val file:RichFile){
      def read2=file.read
    }
    
     //隐式转换不会多次进行,以下的语句会报错
      //不能期望会发生File到RichFile。然后RifchFile到
      //RichFileAnthoer的转换
      val f=new File("file.log").read2
      println(f)

    注意这里指的是源类型到目标类型的转换仅仅会进行一次,并非说不存在多次隐式转换。在一般的方法调用过程中可能会出现多次隐式转换,比如:

    class ClassA {
      override def toString() = "This is Class A"
    }
    class ClassB {
      override def toString() = "This is Class B"
    }
    class ClassC {
      override def toString() = "This is  ClassC"
      def printC(c: ClassC) = println(c)
    }
    class ClassD
    
    object ImplicitWhole extends App {
      implicit def B2C(b: ClassB) = {
        println("B2C")
        new ClassC
      }
      implicit def D2C(d: ClassD) = {
        println("D2C")
        new ClassC
      }
      //以下的代码会进行两次隐式转换
      //由于ClassD中并没有printC方法
      //由于它会隐式转换为ClassC(这是第一次,D2C)
      //然后调用printC方法
      //可是printC方法仅仅接受ClassC类型的參数
      //然而传入的參数类型是ClassB
      //类型不匹配,从而又发生了一次隐式转地换(这是第二次,B2C)
      //从而终于实现了方法的调用
      new ClassD().printC(new ClassB)
    }

    另一种情况也会发生多次隐式转换。假设给函数定义了隐式參数,在实际运行过程中可能会发生多次隐式转换。代码例如以下:

    object Main extends App {
      class PrintOps() {
        def print(implicit i: Int) = println(i);
      }
    
      implicit def str2PrintOps(s: String) = {
        println("str2PrintOps")
        new PrintOps
      }
    
      implicit def str2int(implicit s: String): Int = {
        println("str2int")
        Integer.parseInt(s)
      }
    
      implicit def getString = {
        println("getString")
        "123"
      }
      //以下的代码会发生三次隐式转换
      //首先编译器发现String类型是没有print方法的
      //尝试隐式转换,利用str2PrintOps方法将String
      //转换成PrintOps(第一次)
      //然后调用print方法,但print方法接受整型的隐式參数
      //此时编译器会搜索隐式值。但程序里面没有给定,此时
      //编译器会尝试调用 str2int方法进行隐式转换,但该方法
      //又接受一个implicit String类型參数。编译器又会尝试
      //查找一个相应的隐式值。此时又没有,因此编译器会尝试调用
      //getString方法相应的字符串(这是第二次隐式转换。
      //获取一个字符串,从无到有的过程)
      //得到该字符串后,再调用str2int方法将String类型字符串
      //转换成Int类型(这是第三次隐式转换)
      "a".print
    }

    上面这个样例来源于:爱国者的博客,感谢该作者的无私奉献

    2 要不要用隐式转换的问题

    从上述代码中能够看到,隐式转换功能非常强大,但同一时候也带来了程序复杂性性问题,在一个程序中假设大量运用隐式转换,特别是涉及到多次隐式转换时,会使代码理解起来变得比較困难。那究竟要不要用隐式转换呢?以下给出我自己开发实践中的部分总结,供大家參考:
    1 即使你能轻松驾驭scala语言中的隐式转换,能不用隐式转换就尽量不用
    2 假设一定要用。在涉及多次隐式转换时,必需要说服自己这样做的合理性
    3 假设仅仅是炫耀自己的scala语言能力,请大胆使用

    加入公众微信号,能够了解很多其它最新Spark、Scala相关技术资讯
    这里写图片描写叙述

  • 相关阅读:
    优化后的 google提供的汉字转拼音类(针对某些htc等手机的不兼容情况)
    运行期可以变动大小和尺寸的自定义控件、
    123
    动态创建Ⅱ
    动态创建Ⅰ
    delphi XE5皮肤的使用
    ActionBar
    zhizhensuibi---Source aplikasi database dengan delphi7
    chadang saidui
    30yong wanquan
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7126638.html
Copyright © 2011-2022 走看看