sealed abstract class <:<[-From, +To] extends (From => To) with Serializable private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x } implicit def $conforms[A]: A <:< A = singleton_<:<.asInstanceOf[A <:< A]
它是如何使用的一个例子是TraversableOnce的toMap方法:
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] =
我不明白的是这是如何工作的.我明白A<:< B在句法上等同于类型<:< [A,B].但是我不知道当且仅当A<:B时,编译器才能找到该类型的隐式.我假设$conforms的定义中的asInstanceOf调用使某种方式成为可能,但是如何?此外,使用抽象类的单例实例而不仅仅是使用对象是否重要?
假设我们有以下简单类型层次结构:trait Foo trait Bar extends Foo
我们可以要求证明Bar扩展了Foo:
val ev = implicitly[Bar <:< Foo]
如果我们在-Xprint:typer的控制台中运行它,我们将看到以下内容:
private[this] val ev: <:<[Bar,Foo] = scala.this.Predef.implicitly[<:<[Bar,Foo]](scala.this.Predef.$conforms[Bar]);
所以编译器选择$conforms [Bar]作为我们要求的隐含值.当然这个值的类型为Bar<:<吧,但因为<:<在其第二个类型参数中是协变的,这是Bar<:<的基本类型. Foo,所以它符合要求. (这里有一些神奇的事实,Scala编译器知道如何找到它正在寻找的类型的子类型,但它是一个相当通用的机制,并且在其行为方面并不太令人惊讶.) 现在假设我们要求证明Bar扩展了String:
val ev = implicitly[Bar <:< String]
如果你打开-Xlog-implicits,你会看到:
<console>:9: $conforms is not a valid implicit value for <:<[Bar,String] because: hasMatchingSymbol reported error: type mismatch; found : <:<[Bar,Bar] required: <:<[Bar,String] val ev = implicitly[Bar <:< String] ^ <console>:9: error: Cannot prove that Bar <:< String. val ev = implicitly[Bar <:< String] ^
编译器尝试Bar<:<再次禁止,但由于Bar不是String,因此它不是Bar<:<的子类型字符串,所以它不是我们需要的.但$conforms是编译器可以获得的唯一位置<:<实例(除非我们定义了我们自己的,这将是危险的),所以它非常正确地拒绝编译这个废话. 要解决第二个问题:<:< [ - From,To]类是必需的,因为我们需要此类型类的类型参数才有用.单身Any<:<任何值都可以定义为一个对象 - 使用val和匿名类的决定可以说有点简单,但它是一个你不应该担心的实现细节.
精彩评论