zoukankan      html  css  js  c++  java
  • Scala--隐式转换和隐式参数

    一、隐式转换

    隐式转换函数: 指的是以implicit关键字声明的带有单个参数的函数。这样的函数将被自动应用,将值从一种类型转换为另一种类型

    例子:

    package com.test
    
    
    
    /**
      * Created by Edward on 2018/2/7.
      */
    
    class Fraction(val n : Int, val d : Int){
      def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定义Fraction * Fraction
    }
    
    object Fraction{
      def apply(n : Int, d : Int)= new Fraction(n, d)
    }
    
    object ImplicitFunc {
      //声明需要 implicit 关键字,只有一个参数,自动将参数的类型转换为函数实现的其他类型
      implicit def int2Fraction(n: Int) = Fraction(n, 1)
    }
    
    object Chapter21 {
    
      def main(args: Array[String]) {
    
        import com.test.ImplicitFunc._ //需要 import 函数所在具体的位置
    
        val i = 3 * Fraction(3,2)  //Int类型不能* Fraction类型, Int类型自动转换为Fraction类型
        println(i.n,i.d)
      }
    
    }

    结果:

    (9,2)

    二、利用隐式转换丰富现有类库的功能

    package com.test
    
    import java.io.File
    
    import scala.io.Source
    
    
    /**
      * Created by Edward on 2018/2/7.
      */
    
    
    
    class RichFile(val from:File){
      def read = Source.fromFile(from.getPath).mkString
    }
    
    object Chapter21 {
    
      implicit def file2RichFile (from :File) = new RichFile(from) //隐式转换将原来的File类型转换到这个新的类型;新的类型有read方法。
    
      def main(args: Array[String]) {
    
        val contents = new File("file/wordcount.txt").read
        println(contents)
    
      }
    }

    结果:

    hello    world
    good    morning

    三、引入隐式转换

    Scala会考虑如下的隐式转换函数:

    1.位于源或目标类型的伴生对象中的隐式函数

    2.位于当前作用域可以以单个标识符指代的隐式函数

    object Fraction{
      def apply(n : Int, d : Int)= new Fraction(n, d)
      implicit def int2Fraction(n: Int) = Fraction(n, 1) //隐式转换在伴生对象中
    }

    使用import引入隐式函数  import com.test.Fraction._

    四、隐式转换规则

    隐式转换在以下三种情况下发生:

    1、当表达式的类型与预期的类型不同时

      sqrt(Fraction(1,4))  

        sqrt预期是Double, 将调用fraction2Double

    2、当对象访问一个不存在的成员时

      new File("README").read

        File没有read方法,将调用file2RichFile

    3、当对象调用某个方法,而该方法的参数声明与传入参数不匹配时

      3 * Fraction(4,5)

        Int的*方法不接受Fraction作为参数,将调用int2Fraction

    不会发生隐式转换的情况:

    1、如果代码在不使用隐式转换的前提下通过编译,则不会使用隐式转换

      如: a * b 能够编译,那么编译器不会尝试 a * convert(b) 或 convert(a) * b;

    2、编译器不会同时执行多个转换 

      如: convert(convert(a))*b;

    3、存在二义性的转换是个错误

      如:convert1(a) * b 和 convert2(a) * b;

    package com.test
    
    import java.io.File
    import scala.io.Source
    
    
    /**
      * Created by Edward on 2018/2/7.
      */
    
    class Fraction(val n : Int, val d : Int){
      def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定义Fraction * Fraction
    }
    
    object Fraction{
      def apply(n : Int, d : Int)= new Fraction(n, d)
      implicit def int2Fraction(n: Int) = Fraction(n, 1)
      implicit def fraction2Double(f:Fraction) = f.n * 1.0 / f.d
    }
    
    object Chapter21 {
    
      def main(args: Array[String]) {
    
        import com.chinahadoop.test.Fraction._  //需要 import 隐式函数所在具体的位置
    
        val i = Fraction(3,2) * 3   //转换 Int 为 Fraction 调用 int2Fraction
        println(i)
        val i1 = 3 * Fraction(3,2) //转换 Fraction 为 Int 调用 fraction2Double
        println(i1)
        
      }
    }

    结果:

    com.test.Fraction@6879c0f4
    4.5

    两个类型,优先转换方法*后面的类型

    如果注释掉 如下代码,则需要转换*前面的类型

    //implicit def int2Fraction(n: Int) = Fraction(n, 1)

    结果为:

    4.5
    4.5

    这里不存在二义性,这里隐式转换是针对不同的类型,存在二义性是针对相同的类型有不同的转换方法。

    五、隐式参数

    object Chapter21 {
    
      case class Delimiters(left : String, right : String)
      implicit val quoteDelimiter = Delimiters("<<",">>")
    
      def main(args: Array[String]) {
    
        def quote(what : String)(implicit delimiter : Delimiters) = delimiter.left + what + delimiter.right
    
        println(quote("Hello"))
    
      }
    }

    输出:

    <<Hello>>

    对于给定的数据类型,只有一个隐式的值

    def quota(what: String)(implicit left : String, right : String) // 不正确 

    六、利用隐式参数进行隐式转换

    def smaller[T](a : T, b : T) = if (a < b) a else b

    这种实现方式是不对的,泛型T中不确定 < 方法。

    使用 T到Ordered[T]的隐式转换, Ordered[T] 有接收参数为T的<操作符。

    def smaller[T](a : T, b : T)(implicit order:T => Ordered[T]) = if (order(a) < b) a else b
    println(smaller(10,34))

    理解 

    implicit order:T => Ordered[T]

    Ordered对象提供了从T到Ordered[T]的隐式转换(隐式参数为Ordering[T])

    源码:https://github.com/scala/scala/blob/v2.12.4/src/library/scala/math/Ordered.scala#L1

    object Ordered {
      /** Lens from `Ordering[T]` to `Ordered[T]` */
      implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
        new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
    }

    如果使用自定义类型,我们可以实现Ordered[Fraction]特质的compare方法。

    class Fraction (val n : Int, val d : Int) extends Ordered[Fraction]{
      def * (other:Fraction) =  Fraction(n*other.n,d*other.d) // 定义Fraction * Fraction
      def compare(that:Fraction) = this.n - that.n
    }
    
    def smaller[T](a : T, b : T)(implicit order:T => Ordered[T]) = if (a < b) a else b
    
    println(smaller(Fraction(3,6), Fraction(4,9)))

    order不仅是一个隐式参数,还是一个隐式转换。

    七、上下文界定

    Ordered 、Ordering

    Scala提供两个特质(trait)Ordered与Ordering用于比较。其中,Ordered混入(mix)Java的Comparable接口,而Ordering则混入Comparator接口。
    在Java中:
    实现Comparable接口的类,其对象具有了可比较性;
    实现Comparator接口的类,则提供一个外部比较器,用于比较两个对象。

    Ordered与Ordering的区别与之相类似:
    Ordered特质定义了相同类型间的比较方式,但这种内部比较方式是单一的;
    Ordering则是提供比较器模板,可以自定义多种比较方式。

  • 相关阅读:
    jQuery动画(带参数)
    ajax是什么
    jQuery遍历(3)
    jQuery遍历(2)
    jQuery遍历(1)
    javascript错误处理
    导航菜单的实现
    bootstrap环境搭建
    jQuery动画效果
    MVVM模式下WPF动态绑定展示图片
  • 原文地址:https://www.cnblogs.com/one--way/p/8432624.html
Copyright © 2011-2022 走看看