隱含引數

如果型別的引數應在範圍中定義一次,然後應用於使用該型別值的所有函式,則隱式引數可能很有用。

正常的函式呼叫看起來像這樣:

// 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 等)的隱式引數是不好的做法,因為它會造成混淆並使程式碼的可讀性降低。