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)
有哪些最佳解决方法?引入新特性是一种冗长(特别是对于大型混合链)并导致类型不兼容。添加模拟隐式参数也不是银弹,因为它增加了开销。
更新
我很抱歉,我问了一个错误的问题。我更感兴趣的是所描述错误的性质,而不是其解决方法。类型擦除在哪里发生?我认为代码中没有泛型被认为是类型擦除源。编译器行为背后的机制是什么?
答案 0 :(得分:3)
你想要做的事情有几个问题。
首先,类型擦除发生时并不重要。这是因为类型擦除的概念是,无论是在计算之前还是之后擦除类型,结果都应该相同。对于JVM语言,一些擦除发生在编译时,iinm。
其次,您的“工作”代码实际上并不像您认为的那样工作。这需要对子类型,协方差和逆变量有一点了解。 (Foo <: Bar
表示“Foo是Bar的子类型”,或者非正式地表示“Foo比Bar更具体”)。函数在输入中是逆变的,在输出中是协变的,这意味着仅当A => B <: W => V
和W <: A
时函数B <: V
。
现在考虑一下你的职能:
B1.typeMe(x:T1)
B3.typeMe(x:T3)
B3.typeMe
的输入为T3
,而B1.typeMe
的输入类型为T1
。但是,T3 <: T1
(T3 比T1更具体),因此B3.typeMe
的类型不能是{{1>类型的子类型}}。相反,它们被简单地视为单独的功能,有时会发生一些令人毛骨悚然的无声阴影。老实说,这可能会发出错误或至少发出警告。值得庆幸的是,另一个版本确实如此。