• Scala实践13


    1、隐式参数

          方法可以具有隐式参数列表,由参数列表开头的implicit关键字标记。如果该参数列表中的参数没有像往常一样传递,Scala将查看它是否可以获得正确类型的隐式值,如果可以,则自动传递。

    Scala将查找这些参数的位置分为两类:

    • Scala将首先查找隐式定义和隐式参数,这些参数可以在调用隐式参数块的方法时直接访问(无前缀)。
    • 然后,它查找与隐式候选类型关联的所有伴随对象中隐式标记的成员。
    abstract class Monoid[A] {
      def add(x: A, y: A): A
      def unit: A
    }
    
    object ImplicitTest {
      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)))       // intMonoid
        println(sum(List("a", "b", "c"))) //stringMonoid
      }
    }

       Monoid定义一个add在这里调用的操作,它返回一个A,一个unit能够创建一些(特定)的操作。

    为了说明隐式参数如何工作,首先分别定义 stringMonoidintMonoid字符串和整数。该implicit关键字指示对应的对象可以被隐式地使用。

    该方法sum接受 List[A]并返回A,它从初始化A开始unit,并将A列表中的每个下一个与该add方法的下一个组合。使参数m隐含在这里意味着只需要在调用方法时提供参数,如果Scala可以找到隐Monoid[A]m参数用于隐式参数。

    main方法中,调用sum两次,并且只提供参数。Scala现在将在上述范围内寻找隐含的内容。第一次调用sum传递List[Int]for xs,这意味着A就是Int。隐藏了隐式参数列表m,因此Scala将查找隐式类型Monoid[Int]。第一个查找规则读取

    • Scala将首先查找隐式定义和隐式参数,这些参数可以在调用隐式参数块的方法时直接访问(无前缀)。

       intMonoid是一个可以直接访问的隐式定义。它也是正确的类型,因此它会sum自动传递给方法。

    第二次调用sum传递List[String],这意味着A就是String。隐式查找将与使用相同的方式Int,但这次将查找stringMonoid,并自动传递为m

    输出如下:

    2、隐式转换

        从类型S到类型的隐式转换由T具有函数类型的隐式值定义S => T,或者由可转换为该类型的值的隐式方法定义。

    隐式转换适用于两种情况:

    • 如果表达式e是类型S,并且S不符合表达式的预期类型T
    • e.m具有e类型S的选择m中,如果选择器不表示成员S

           在第一种情况下,c搜索适用e于其结果类型符合的转换T。在第二种情况下,c搜索适用于e且其结果包含名为的成员的转换m

           如果隐式方法List[A] => Ordered[List[A]]在范围内,以及隐式方法Int => Ordered[Int],则对两个类型列表的以下操作List[Int]是合法的:

    List(1, 2, 3) <= List(4, 5)
    

      Int => Ordered[Int]通过自动提供隐式方法scala.Predef.intWrapperList[A] => Ordered[List[A]]下面提供了隐式方法的示例。

    import scala.language.implicitConversions
    
    implicit def list2ordered[A](x: List[A])
        (implicit elem2ordered: A => Ordered[A]): Ordered[List[A]] =
      new Ordered[List[A]] { 
        //替代
        def compare(that: List[A]): Int = 1
      }
    

      隐式导入的对象scala.Predef声明了常用类型(例如scala.collection.immutable.Map别名Map)和方法(例如assert)的几个别名,但也有几个隐式转换。

           例如,在调用期望的Java方法时java.lang.Integer,您可以自由地传递它scala.Int。这是因为Predef包含以下隐式转换:

    import scala.language.implicitConversions
    
    implicit def int2Integer(x: Int) =
      java.lang.Integer.valueOf(x)
    

      

    因为如果不加选择地使用隐式转换可能会有陷阱,编译器会在编译隐式转换定义时发出警告。

    要关闭警告,请执行以下任一操作:

    • 导入scala.language.implicitConversions隐式转换定义的范围
    • 使用调用编译器 -language:implicitConversions

    编译器应用转换时不会发出警告。

    3、多态方法

        Scala中的方法可以按类型和值进行参数化。语法类似于泛型类。类型参数括在方括号中,而值参数括在括号中。例子如下:

    def listOfDuplicates[A](x: A, length: Int): List[A] = {
      if (length < 1)
        Nil
      else
        x :: listOfDuplicates(x, length - 1)
    }
    println(listOfDuplicates[Int](3, 4)) 
    println(listOfDuplicates("La", 8)) 
    

     结果如下:

     

          该方法listOfDuplicates采用类型参数A和值参数xlength。价值x是类型A。如果length < 1返回一个空列表。否则,前缀x到递归调用返回的重复列表。(注意,这::意味着将左侧的元素添加到右侧的列表中。)

    例1中,通过写入显式提供了类型参数[Int]。因此,第一个参数必须是a Int,返回类型将是List[Int]

    例2显示您并不总是需要显式提供type参数。编译器通常可以根据上下文或值参数的类型来推断它。在这个例子中,"La"是一String所以编译器知道A必须String

     4、类型推断

            Scala编译器通常可以推断表达式的类型,因此您不必显式声明它。

    •  省略类型
    val businessName = "Café"
    

      编译器可以检测到它businessName是一个String。它与方法类似:

    def squareOf(x: Int) = x * x

           编译器可以推断返回类型是 Int,因此不需要显式返回类型。

             

         编译器无法对递归方法推断结果类型。这是一个程序,由于这个原因将使编译器失败:

    def fac(n: Int) = if (n == 0) 1 else n * fac(n - 1)

          在调用多态方法或实例化泛型类时,也不必强制指定类型参数。Scala编译器将从上下文和实际方法/构造函数参数的类型推断出这样的缺失类型参数。

    这是两个例子:

    case class MyPair[A, B](x: A, y: B);
    val p = MyPair(1, "scala") //多态方法
    
    def id[T](x: T) = x
    val q = id(1)  //泛型类
    

      编译器使用参数的类型MyPair来确定哪种类型A和类型B。同样的类型x

    • 参数

             编译器永远不会推断出方法参数类型。但是,在某些情况下,当函数作为参数传递时,它可以推断出匿名函数参数类型。

    Seq(1, 3, 4).map(x => x * 2)
    

      map的参数是f: A => B。因为我们把整数放在中Seq,所以编译器知道那AInt(即它x是一个整数)。因此,编译器可以推断从x * 2B是类型Int

    • 不依赖类型推断

           通常认为声明在公共API中公开的成员类型更具可读性。因此,我们建议您为将向您的代码用户公开的任何API明确指定类型。

           此外,类型推断有时可以推断出太具体的类型。假设我们写道:

    var obj = null
    

      我们不能继续进行这种重新分配:

    obj = new AnyRef
    

      它不会编译,因为推断的类型objNull。由于该类型的唯一值是null,因此无法分配不同的值。

  • 相关阅读:
    php公立转农历
    php判断客户端浏览器类型
    php为图片添加水印
    php类精确验证身份证号码
    webpack脚手架搭建(简单版)
    《深入浅出Node.js》第4章 异步编程
    js 实现继承相关
    设计模式:观察者模式
    bind 方法实现
    用 CSS 实现字符串截断
  • 原文地址:https://www.cnblogs.com/0205gt/p/11039548.html
Copyright © 2020-2023  润新知