• 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. 隐式参数的函数定义使用起来更加灵活,因为它是由调用方决定输入的值;带缺省值的参数的函数相对僵化,在函数定义时就决定了缺省值。
  • 相关阅读:
    windows服务(installutil.exe)报错。异常来自 HRESULT:0x80131515
    挖掘微信Web版通信的全过程 [转]
    MVC3升级为MVC4
    MFC与C#连接MYSQL乱码问题
    MFC ADO连接Sql Server数据库报无效指针的问题
    PPM解码器
    Verilog case、casez、casex
    任务和函数
    verilog $*命令
    Verilog 带有parameter的模块端口调用
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/14814009.html
Copyright © 2020-2023  润新知