In [1]:
// 需求场景:
// 提供一个计算销售税的方法
// 条件:
// 1. 某些应用需要知道当前事务发生的具体地点,以便增收税
// 2. 为了促进购物消费,某些地方可能将年假最后几天定位“免税期”
def calcTax(amount: Float)(implicit rate: Float): Float = amount*rate
object SimpleStateSalesTax {
implicit val rate: Float = 0.05F
}
case class ComplicatedSalesTaxData(
baseRate: Float,
isTaxHoliday: Boolean,
storeId: Int
)
object ComplicatedSalesTax {
private def extraTaxRateForStore(id: Int): Float = {
// 可以通过id推断商铺地点,再计算附加税
0.0F
}
implicit def rate(implicit cstd: ComplicatedSalesTaxData): Float =
if (cstd.isTaxHoliday) 0.0F
else cstd.baseRate + extraTaxRateForStore(cstd.storeId)
}
In [2]:
{
import SimpleStateSalesTax.rate
val amount = 100F
println(s"Tax on $amount = ${calcTax(amount)}")
}
In [3]:
{
import ComplicatedSalesTax.rate
implicit val myStore = ComplicatedSalesTaxData(0.06F, false, 1010)
val amount = 100F
println(s"Tax on $amount = ${calcTax(amount)}")
}
In [4]:
import math.Ordering
case class MyList[A](list: List[A]) {
def sortBy1[B](f: A => B)(implicit ord: Ordering[B]): List[A] =
list.sortBy(f)(ord)
def sortBy2[B: Ordering](f: A => B): List[A] =
list.sortBy(f)(implicitly[Ordering[B]])
}
In [5]:
val list = MyList(List(1, 3, 5, 2, 4))
In [6]:
list sortBy1 (i => -i)
In [7]:
list sortBy2 (i => -i)
MyList类提供了两种sorBy方法:
B: Ordering
被上下文定界(context bound),它安置隐式参数列表将接受Ordering[B]实例。implicitly方法会对传给函数的所有标记为隐式参数的实例进行解析。
object M {
def m(seq: Seq[Int]): Unit = println(s"Seq[Int]: $seq")
def m(seq: Seq[String]): Unit = println(s"Seq[String]: $seq")
}
将会出现这样的报错:
Compilation Failed
Main.scala:50: double definition:
def m(seq: Seq[Int]): Unit at line 49 and
def m(seq: Seq[String]): Unit at line 50
have same type after erasure: (seq: Seq)Unit
def m(seq: Seq[String]): Unit = println(s"Seq[String]: $seq")
^
因为上面的两个m方法的字节码是一样的,编译器不允许同时出现这些方法定义。
不过我们可以添加隐式参数来消除这些方法的二义性。
In [8]:
object M {
implicit object IntMarker
implicit object StringMarker
def m(seq: Seq[Int])(implicit i: IntMarker.type): Unit = println(s"Seq[Int]: $seq")
def m(seq: Seq[String])(implicit s: StringMarker.type): Unit = println(s"Seq[String]: $seq")
}
In [9]:
import M._
In [10]:
m(List(1, 2, 3))
In [11]:
m(List("one", "two", "three"))
In [12]:
IntMarker
In [13]:
StringMarker
为了尽量避免使用Int和String这样常用类型作为隐式参数和对应值(这些类型还可能出现多处定义,而导致二义性和编译器抛出错误),比较安全的做法是专门设计特有的类型作为隐式参数。
@implicitNotFound注解告诉编译器在不能构造出带有该注解的类型的参数时给出错误提示。这样做事给程序员有意义的错误提示。
比如CanBuildFrom构造器和<:<类有相关注解:
@implicitNotFound(msg = "Cannot construct a collection of type ${To} with elements of type ${Elem} based on a collection of type ${From}.")
trait CanBuildFrom[-From, -Elem, +To] {...}
@implicitNotFound(msg = "Cannot prove that ${From} <:< ${To}.")
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable