Scala隐式参数

示例

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

正常的函数调用如下所示:

// 导入持续时间方法
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库。

通常,将隐式参数与基本类型(如IntLongString等)一起使用是不明智的做法,因为这会造成混乱,并使代码的可读性降低。