隐含参数
如果类型的参数应在范围中定义一次,然后应用于使用该类型值的所有函数,则隐式参数可能很有用。
正常的函数调用看起来像这样:
// import the duration methods
import scala.concurrent.duration._
// a normal method:
def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis
val timeout = 1.second
// timeout: scala.concurrent.duration.FiniteDuration = 1 second
// to call it
doLongRunningTask(timeout) // 1000
现在假设我们有一些方法都具有超时持续时间,我们希望使用相同的超时调用所有这些方法。我们可以将超时定义为隐式变量。
// import the duration methods
import scala.concurrent.duration._
// dummy methods that use the implicit parameter
def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis
def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis
// we define the value timeout as implicit
implicit val timeout: FiniteDuration = 1.second
// we can now call the functions without passing the timeout parameter
doLongRunningTaskA() // 1000
doLongRunningTaskB() // 1000
这种方式的工作方式是 scalac 编译器在作用域中查找一个值,该值被标记为隐式,其类型与隐式参数的类型匹配。如果找到一个,它将把它作为隐含参数应用。
请注意,如果在作用域中定义两个或更多相同类型的含义,则此操作无效。
要自定义错误消息,请在类型上使用 implicitNotFound
注释:
@annotation.implicitNotFound(msg = "Select the proper implicit value for type M[${A}]!")
case class M[A](v: A) {}
def usage[O](implicit x: M[O]): O = x.v
//Does not work because no implicit value is present for type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
implicit val first: M[Int] = M(1)
usage[Int] //Works when `second` is not in scope
implicit val second: M[Int] = M(2)
//Does not work because more than one implicit values are present for the type `M[Int]`
//usage[Int] //Select the proper implicit value for type M[Int]!
超时是一个常见的用例,或者例如在 Akka 中 ,ActorSystem(大多数时候)总是相同的,所以它通常是隐式传递的。另一种使用情况是库的设计,最常见的是依赖于类型类(如 FP 库 scalaz ,猫或破裂 )。
通常认为使用基本类型(如 Int , Long , String 等)的隐式参数是不好的做法,因为它会造成混淆并使代码的可读性降低。