【问题标题】:Understanding Tagged Types and asInstanceOf了解标记类型和 asInstanceOf
【发布时间】:2016-04-22 05:32:06
【问题描述】:

我使用 Miles Sabin gist 中的标记类型:

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 方法有什么魔力?

【问题讨论】:

  • { type Tag = MyTrait } 在运行时并不真正存在,它被完全擦除并且仅由 Scala 编译器考虑。
  • 那么当s.asInstanceOf[String @@ MyTrait]被调用时运行时发生了什么?

标签: scala functional-programming scalaz


【解决方案1】:

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

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 } 是如何匹配的:

    不属于上述形式之一的类型也被接受为类型模式。然而,这样的类型模式将被转换为它们的擦除。 Scala 编译器将对这些模式发出“未经检查”的警告,以标记可能丢失类型安全。

  3. Finally,

    复合类型T1 with … with Tn {R}的擦除是T1,…,Tn的交集支配者的擦除。

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

【讨论】:

  • 所以在类型擦除后s.asInstanceOf[String @@ MyTrait]变成s.asInstanceOf[String]?
  • @ka4eli 是的。 isInstanceOf、模式匹配等也是如此。
  • @ka4eli 我已经用一些细节扩展了答案。
  • 这是一个有效的类型表示法:AnyRef { type Tag = MyTrait } ?
猜你喜欢
  • 2018-06-18
  • 1970-01-01
  • 2011-10-01
  • 2018-06-18
  • 1970-01-01
  • 2020-08-14
  • 1970-01-01
  • 2011-05-30
  • 1970-01-01
相关资源
最近更新 更多