运维开发网

<:<运算符如何在Scala中运行?

运维开发网 https://www.qedev.com 2020-06-18 08:01 出处:网络 作者:运维开发网整理
在 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
在 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]

它是如何使用的一个例子是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和匿名类的决定可以说有点简单,但它是一个你不应该担心的实现细节.

0

精彩评论

暂无评论...
验证码 换一张
取 消