这是因为这种类型的见证也是一个函数。它被声明Predef为:
sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
所以A <:< JsValue也是一个函数(A) => JsValue。你可能想知道这个函数做了什么:它什么都不做,它接受 aA并直接返回它(作为 a JsValue)。
要了解为什么这很有用,请考虑以下示例:
sealed trait Root { def bip() { println("bip") } }
def makeBip[A <: Root](a: A) {
a.bip() // works because a is known to the type system to be a Root
}
def makeBip2[A](a: A)(implicit ev: A <:< Root) {
a.bip() // works, because implicit resolution turns it into `ev(a).bip()`
}
如果没有隐式,最后一种方法makeBip2将无法编译,因为即使您知道这a要Root归功于证据,但类型系统却没有。你可以投射它,它肯定会起作用:
def makeBip3[A](a: A)(implicit ev: A <:< Root) {
a.asInstanceOf[Root].bip() // ugly
}
但这感觉不对。如果您有办法转换a为Root……但是等等,您确实做到了:证据本身!
def makeBip4[A](a: A)(implicit ev: A <:< Root) {
ev(a).bip() // works!
}
并且由于隐式参数可用作方法中的隐式参数,a.bip()因此将自动转换为ev(a).bip()并且您永远不需要知道涉及的函数。
但是,类型系统仅使用隐式将 an 解析A为 a JsValue,而不是 a Seq[A]into aSeq[JsValue]或 a Reads[A]into a Reads[JsValue]。
因此,在您的情况下,this.map(witness)只需通过应用什么都不做的函数来让类型系统了解 aReads[A]是 a Reads[JsValue],这样它就可以与接受 aJsValue并返回 a 的东西组合在一起B。
有关更多信息,请参阅 SO 上的广义类型约束问题。