【问题标题】:When exactly type erasure occurs?什么时候发生类型擦除?
【发布时间】:2012-03-17 08:09:26
【问题描述】:

scala 的语法很有前途。我最初认为 scala 不仅仅是舒适的 java 并且可以引入全新的编程范式,但是语法允许的许多功能在语义上是不正确的。所以我开始寻找 scala 的限制和技巧来消除它们。尽管我只是编写了一个测试项目以从其他角度适应 scala 方式和模式。

主要障碍是继承自 jvm 实现的类型擦除。我可以写一篇小文章,题为《类型擦除毁掉的十种美丽图案》。据说类型擦除会污染泛型,但我偶然发现了 mixins 中的类型擦除。我认为这是在 scala 中实现 mixin 的问题。

序言

trait T1
trait T2

trait B1 {
  def typeMe(x:T1){}
}

使用 mixins 破坏代码

trait B2 extends B1 {
  def typeMe(x:T1 with T2) {}
}

使用 mixin 声明为单独特征的工作代码

trait T3 extends T1 with T2
trait B3 extends B1 {
  def typeMe(x:T3) {}
}

错误是:

error: name clash between defined and inherited member:
method typeMe:(x: T1 with T2)Unit and
method typeMe:(x: T1)Unit in trait B1
have same type after erasure: (x: $line12.$read#$iw#$iw#T1)Unit
    def typeMe(x:T1 with T2)

可用的最佳解决方法是什么?引入新特性是冗长的(尤其是对于大型 mixin 链)并导致类型不兼容。添加模拟隐式参数也不是灵丹妙药,因为它会增加开销。


更新:

非常抱歉,我问错了问题。我对所描述错误的性质更感兴趣,而不是其解决方法。类型擦除发生在哪里?我在被认为是类型擦除源的代码中没有看到泛型。编译器行为背后的机制是什么?

【问题讨论】:

  • 您可以考虑替代重载方法;以不同的方式命名它们或使用 typeclass 模式。
  • 我希望你意识到在B3 中你不是从B1 覆盖方法typeMe,而是重载它以获取子类型。虽然这是合法的,但这是一个坏主意,因为调用的方法将取决于局部变量的类型。即假设B3对象b3T3对象t3b3.typeMe(t3)b3.typeMe(t3: T1)将调用不同的方法。

标签: scala mixins type-erasure


【解决方案1】:

你正在尝试做的事情有几个问题。

首先,何时发生类型擦除并不重要。这是因为类型擦除的概念是,无论您是在计算之前还是之后擦除类型,结果都应该是相同的。对于 JVM 语言,一些擦除发生在编译时,iinm。

其次,您的“工作”代码实际上并不像您认为的那样工作。这需要对子类型、协变和逆变有一点了解。 (Foo <: Bar 的意思是“Foo 是 Bar 的子类型”,或者非正式地,“Foo 比 Bar 更具体”)。函数的输入是逆变的,输出是协变的,这意味着函数 A => B <: W => V 仅当 W <: AB <: V 时。

现在考虑你的函数:

B1.typeMe(x:T1)
B3.typeMe(x:T3)

B3.typeMe 的输入是T3,而B1.typeMe 的输入类型是T1。但是,T3 <: T1(T3 比 T1更具体),因此B3.typeMe 的类型不能B1.typeMe 类型的子类型。相反,它们只是被认为是独立的功能,有时会出现一些令人毛骨悚然的无声阴影。老实说,这可能会发出错误或至少发出警告。谢天谢地,其他版本可以。

【讨论】:

  • 我写的不是实际问题。主要问题是类型擦除发生在哪里,错误背后的泛型是什么。
猜你喜欢
  • 2023-03-29
  • 2018-08-27
  • 2018-02-18
相关资源
最近更新 更多