如果应在范围中一次定义类型的参数,然后将其应用于使用该类型的值的所有函数,则隐式参数会很有用。
正常的函数调用如下所示:
// 导入持续时间方法 import scala.concurrent.duration._ // 正常方法: def doLongRunningTask(timeout: FiniteDuration): Long = timeout.toMillis val timeout = 1.second // 超时:scala.concurrent.duration.FiniteDuration = 1秒 // 称呼它 doLongRunningTask(timeout) // 1000
现在假设我们有一些方法都具有超时持续时间,并且我们希望使用相同的超时来调用所有这些方法。我们可以将超时定义为隐式变量。
// 导入持续时间方法 import scala.concurrent.duration._ // 使用隐式参数的虚拟方法 def doLongRunningTaskA()(implicit timeout: FiniteDuration): Long = timeout.toMillis def doLongRunningTaskB()(implicit timeout: FiniteDuration): Long = timeout.toMillis // 我们将值超时定义为隐式 implicit val timeout: FiniteDuration = 1.second // 我们现在可以在不传递timeout参数的情况下调用函数 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 //不起作用,因为类型M [Int]没有隐式值 //usage[Int] //为类型M [Int]选择适当的隐式值! implicit val first: M[Int] = M(1) usage[Int] //当“秒”不在范围内时起作用 implicit val second: M[Int] = M(2) //不起作用,因为类型M [Int]存在多个隐式值 //usage[Int] //为类型M [Int]选择适当的隐式值!
超时是通常的用例,例如在Akka中,ActorSystem(在大多数情况下)总是相同的,因此通常会隐式传递。另一个用例是库设计,最常见的是依赖类型类(如scalaz,cat或rapture)的FP库。
通常,将隐式参数与基本类型(如Int,Long,String等)一起使用是不明智的做法,因为这会造成混乱,并使代码的可读性降低。