隐式参数
简单隐式参数实现如下: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来证明类型时,类型推断过程不会像上面那样再向上寻找可能满足的情况,而直接报错。