隱含引數
如果型別的引數應在範圍中定義一次,然後應用於使用該型別值的所有函式,則隱式引數可能很有用。
正常的函式呼叫看起來像這樣:
// 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 等)的隱式引數是不好的做法,因為它會造成混淆並使程式碼的可讀性降低。