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相关技术资讯
    这里写图片描写叙述

  • 相关阅读:
    POJ 3258 (NOIP2015 D2T1跳石头)
    POJ 3122 二分
    POJ 3104 二分
    POJ 1995 快速幂
    409. Longest Palindrome
    389. Find the Difference
    381. Insert Delete GetRandom O(1)
    380. Insert Delete GetRandom O(1)
    355. Design Twitter
    347. Top K Frequent Elements (sort map)
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7126638.html
Copyright © 2011-2022 走看看