隱式參數
簡單隱式參數實現如下:implicit
隱式傳入參數
def calcTa2(a: Int)(implicit b: Int): Float = a*b
implicit val t = 3
println(calcTa2(10))
隱式轉換函數
對double值進行隱式轉換
object ImplicitTest extends App {
implicit def double2int(d: Double) = d.toInt
val x: Int = 3.5
println(x)
}
隱式調用函數
object ImplicitApp extends App {
object SimpleSateSalesTax {
implicit val rate: Float = 0.5F
}
def calcTax(amount: Float)(implicit rate: Float): Float = amount * rate
case class ComplicatedSalesTaxData(baseRate: Float, isTaxHoliday:Boolean, storeId:Int)
object ComplicatedSalesTax {
private def extraTaxRateForStore(id :Int) : Float = {
val d = id match {
case 1 => 0.2F
case 2 => 0.8F
}
d
}
implicit def rate(implicit cstd: ComplicatedSalesTaxData) : Float =
if(cstd.isTaxHoliday) 0.0f
else cstd.baseRate + extraTaxRateForStore(cstd.storeId)
}
{
import SimpleSateSalesTax.rate
val amount = 100F
println(s"Tax on $amount = ${calcTax(amount)}")
}
{
import ComplicatedSalesTax.rate
implicit val myStore = ComplicatedSalesTaxData(0.06F, false, 2)
val amount = 100F
println(s"Tax on $amount = ${calcTax(amount)}")
}
/**
* 簡單隱式參數
* @param a
* @param b
* @return
*/
def calcTa2(a: Int)(implicit b: Int): Float = a*b
implicit val t = 3
println(calcTa2(10))
}
隱式轉換的時機:
- 當方法中的參數的類型與目標類型不一致時
- 當對象調用類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換
隱式解析機制
即編譯器是如何查找到缺失信息的,解析具有以下兩種規則:
- 首先會在當前代碼作用域下查找隱式實體(隱式方法 隱式類 隱式對象)
- 如果第一條規則查找隱式實體失敗,會繼續在隱式參數的類型的作用域裏查找
類型的作用域是指與該類型相關聯的全部伴生模塊,一個隱式實體的類型T它的查找範圍如下:
- 如果T被定義爲T with A with B with C,那麼A,B,C都是T的部分,在T的隱式解析過程中,它們的伴生對象都會被搜索;
- 如果T是參數化類型,那麼類型參數和與類型參數相關聯的部分都算作T的部分,比如List[String]的隱式搜索會搜索List的;
伴生對象和String的伴生對象; - 如果T是一個單例類型p.T,即T是屬於某個p對象內,那麼這個p對象也會被搜索;
- 如果T是個類型注入S#T,那麼S和T都會被搜索;
隱式參數所遵循的規則
- 只有最後一個參數列表中允許出現隱式參數,這也適用用於只有一個參數列表的情況。
- implicit關鍵字必須出現在參數列表的最左邊,而且只能出現一次。列表中出現在implicit關鍵字之後的參數都不是“非隱式”的。
- 假如參數列表以implicit關鍵字開頭,那麼所有的參數都是隱式的。
“<:” 和 “>:” 和 “”作用及用法
object BorderApp extends App {
def test[T](i: T)(implicit ev: T <:< java.io.Serializable) {
println("OK")
}
test("22")
}
隱式與類型推導 <:與<:<的差異
scala> def foo[A, B <: A](a: A, b: B) = (a,b)
foo: [A, B <: A](a: A, b: B)(A, B)
scala> foo(1, List(1,2,3))
res0: (Any, List[Int]) = (1,List(1, 2, 3))
傳入第一個參數是Int類型,第二個參數是List[Int]
,顯然這不符合B <: A
的約束,編譯器在做類型推導的時候,爲了滿足這個約束,會繼續向上尋找父類型來匹配是否滿足,於是在第一個參數被推導爲Any
類型的情況下,List[Int]
符合Any
的子類型。
scala> def bar[A,B](a: A, b: B)(implicit ev: B <:< A) = (a,b)
bar: [A, B](a: A, b: B)(implicit ev: B <:< A)(A, B)
scala> bar(1,List(1,2,3))
<console>:13: error: Cannot prove that List[Int] <:< Int.
bar(1,List(1,2,3))
^
通過隱式參數ev來證明類型時,類型推斷過程不會像上面那樣再向上尋找可能滿足的情況,而直接報錯。