了解标记类型和 asInstanceOf

Understanding Tagged Types and asInstanceOf

我使用来自 Miles Sabin 要点的标记类型:

type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]

trait MyTrait

def tag(s: String): String @@ MyTrait = s.asInstanceOf[String @@ MyTrait]

我可以像这样使用(并且有效):

scala> tag("lala")
res7: @@[String,MyTrait] = lala

我的问题是:如何?这怎么不抛ClassCastexception:s.asInstanceOf[String @@ MyTrait]。在我看来,"lala" 是 String 类型但不是 String with { type Tag = MyTrait} 类型,因为它像通常的 String 对象一样被实例化。 asInstanceOf 方法有什么神奇之处?

首先,请注意标记类型的全部意义在于避免运行时开销,但这也意味着您不能指望运行时类型检查能够区分它们!

asInstanceOf 是运行时转换,JVM 不知道 Scala 类型系统(甚至 Java 的);它只有 类、接口和原语。所以 asInstanceOf 只能转换为 erased 类型,即最接近 Scala 类型的 JVM 等价物。 String with { type Tag = MyTrait}的擦除类型为String,所以成功。

规范的相关部分是:

  1. Standard Library定义asInstanceOf如下:

    /** Type cast; needs to be inlined to work as given */
    def asInstanceOf[A]: A = this match {
      case x: A => x
      case _ => if (this eq null) this
                else throw new ClassCastException()
    }
    
  2. Type patterns解释了x: String with { type Tag = MyTrait }是如何匹配的:

    Types which are not of one of the forms described above are also accepted as type patterns. However, such type patterns will be translated to their erasure. The Scala compiler will issue an "unchecked" warning for these patterns to flag the possible loss of type-safety.

  3. Finally,

    The erasure of a compound type T1 with … with Tn {R} is the erasure of the intersection dominator of T1,…,Tn.

    在这种情况下T1StringT2AnyRef { type Tag = MyTrait },所以你得到StringAnyRef的交集支配符, 即 String.