zoukankan      html  css  js  c++  java
  • Scala 进阶(3)—— implicit 用法:隐式参数

    1. 隐式参数

    Scala 中的 implicit 关键字,除了能够加在 class 之前作为一个类的隐式转换之外,见:Scala 进阶(2)—— implicit 用法:隐式转换

    还能够加在参数之前,定义一个隐式参数。

    2. 柯里化

    隐式参数的应用是基于一个柯里化的函数定义,所以先简单地介绍一下柯里化函数。

    简而言之,一个柯里化的函数,允许你拥有多个参数列表。

    看下面这个例子:

    object CreateProcess {
    
      def sendCreate1(req: Request)(re: RequestEngine)(ec: ExecutionContext) = {
    
      }
    
      def sendCreate2(req: Request, re: RequestEngine, ec: ExecutionContext) = {
    
      }
    }

    sendCreate1 就是一个柯里化的函数,于 sendCreate2 相比,它拥有三个参数列表,其本质是当你调用 sendCreate1 这个函数时,进行了三次常规的函数调用,即:

    • 第一次调用 sendCreate1(req),返回一个接受入参为 RequestEngine 的函数 XXX1。
    • 第二次调用 XXX1(re),返回一个接受入参为 ExecutionContext 的函数 XXX2。
    • 第三次调用 XXX2(ec),返回整个函数的返回值,在这里是 Unit。

    而隐式参数,提供的是整个最后一组(不是最后一个)柯里化的参数列表:

      def sendCreate3(req: Request)(implicit re: RequestEngine, ec: ExecutionContext) = {
    
      }

    上面这个例子,re 和 ec 都被标记为了 implicit。

    3. 隐式参数能做什么?

    知道了隐式参数是什么,接下来需要做的就是要知道它能做些什么。

    一句话来概括,就是被标记为 implicit 的参数列表可以被隐式地提供(当然也可以显示地提供,就像正常调用一个柯里化的函数)。

    我们看一下,如何去调用以上 sendCreate3 这个函数:

    object Main {
      def main(args: Array[String]): Unit = {
        val req = new Request()
        val re = new RequestEngine()
        val ec = ExecutionContext.fromExecutor(
          new Executor {
            def execute(runnable: Runnable): Unit = runnable.run()
          })
        CreateProcess.sendCreate3(req)(re, ec) // 显式使用 implicit 参数
      }
    }

    这是显示地使用隐式参数,不过既然我们使用了 implicit,一般来说是不推荐这么用的。

    然后看一下如何隐式地使用 implicit 参数:

    
    
    package implicitdemo

    object Main { def main(args: Array[String]): Unit
    = { val req = new Request() CreateProcess.sendCreate3(req) // 隐式使用 implicit 参数
    } }

    可以发现上面这个例子,没有给 sendCreate3 的最后一个参数列表赋值,

    究其原因,是因为在它的作用域中能够找到一个(并且只能是一个,如果有两个或以上,编译器会拒绝寻找隐式参数)类型为 RequestEngine 和 类型为 ExecutionContext 的参数,

    并且,这两个参数在声明的时候用了 implicit 修饰:

    import java.util.concurrent.Executor
    import scala.concurrent.ExecutionContext
    
    package object implicitdemo {
    
      implicit val re = new RequestEngine()
      implicit val ec = ExecutionContext.fromExecutor(
        new Executor {
          def execute(runnable: Runnable): Unit = runnable.run()
        })
    }

    4. 类 class 上的隐式参数

    声明隐式参数的 implicit 关键字,也可以加在类参数上面,使用的方法和加在函数上一致,显式调用,或在作用域中存在 implicit 修饰的相同类型参数:

    class RequestEngine(implicit ec: ExecutionContext) {
    
      def send(req: Request) = {
        Future {
          // Do something
        }
      }
    }
    class RequestEngine {
    
      def send(req: Request)(implicit ec: ExecutionContext) = {
        Future {
          // Do something
        }
      }
    }

    上面两种从功能上说是等价的。

    5. 和带缺省参数值的函数对比

    Scala 允许定义函数时,带缺省的参数值,例如:

      val requestEngine = new RequestEngine()
      val executionContext = ExecutionContext.fromExecutor(
        new Executor {
          def execute(runnable: Runnable): Unit = runnable.run()
        })
    
      def sendCreate4(req: Request,
                      re: RequestEngine = requestEngine,
                      ec: ExecutionContext = executionContext) = {
        
      }
    object Main {
      def main(args: Array[String]): Unit = {
        val req = new Request()
        CreateProcess.sendCreate4(req)
      }
    }

    带缺省参数的函数,和带隐式参数的函数,从功能上来说及其类似,如果说有区别,个人总结了以下几点:

    1. 隐式参数必须用在柯里化的函数定义中;带缺省值的参数更多用在非柯里化的函数定义中。
    2. 隐式参数的函数,调用时没有显式或隐式地提供 implicit 参数,会编译错误;带缺省值的参数,没有给则直接使用默认值,所以相对前者更加安全。
    3. 隐式参数的函数定义使用起来更加灵活,因为它是由调用方决定输入的值;带缺省值的参数的函数相对僵化,在函数定义时就决定了缺省值。
  • 相关阅读:
    Linux内核网络协议栈优化总纲
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 连续正整数的和
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 寂寞的数
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 学做菜
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 判断字符位置
    Java实现 蓝桥杯VIP 算法训练 链表数据求和操作
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/14814009.html
Copyright © 2011-2022 走看看