隐含参数

如果类型的参数应在范围中定义一次,然后应用于使用该类型值的所有函数,则隐式参数可能很有用。

正常的函数调用看起来像这样:

// 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破裂 )。

通常认为使用基本类型(如 IntLongString 等)的隐式参数是不好的做法,因为它会造成混淆并使代码的可读性降低。