在Scala中,有一个类<:<
见证了类型约束。来自Predef.scala
:
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]
toMap
TraversableOnce
def toMap[T, U](implicit ev: A <:< (T, U)): immutable.Map[T, U] =
方法中使用的示例:
A <:< B
我不明白这是如何运作的。我理解<:<[A, B]
在语法上等同于A <: B
类型。但是,当且仅当asInstanceOf
时,我不知道编译器如何找到隐含的类型。我假设$conforms
定义中的object
调用使某种方式成为可能,但是如何?此外,使用抽象类的单例实例而不仅仅使用{{1}}是否很重要?
答案 0 :(得分:12)
假设我们有以下简单类型层次结构:
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
,但由于<:<
在其第二个类型参数中是协变的,因此它是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
,但由于Bar
不是String
,因此这不是Bar <:< String
的子类型,所以它不是我们需要的。但是$conforms
是编译器可以获得<:<
实例的唯一地方(除非我们已经定义了自己的实例,这将是危险的),所以它非常正确地拒绝编译这些废话。
要解决第二个问题:<:<[-From, +To]
类是必需的,因为我们需要此类型类的类型参数才有用。单例Any <:< Any
值也可以定义为对象 - 使用val
和匿名类的决定可以说有点简单,但它是一个你不应该需要的实现细节担心。