这不仅是因为A with B
必须被视为一种新类型。对于 Scala 类型系统,是否存在与A with B
. 生成了一个匿名类,因为它必须包含已混入的特征中所有方法的桥接方法。
创建匿名类的原因是该对象必须具有 from 的所有方法和 from 的A
所有方法的实现B
。在 JVM 字节码级别,这将保证继承多个类,并且 JVM 不支持多继承模型。
为了模拟多重继承(或 mixin 组合,无论你怎么称呼它),Scala 在你创建一个 trait 时会做以下事情:
- 如果 trait
T
没有方法实现,它会创建一个接口来定义 trait 中的所有方法。
如果 traitT
有方法实现,它会另外创建一个类,该类T$class
对T
. 此静态方法与 中的相应方法具有相同的主体T
,但其签名已更改为包含this
参数。如果T
有:
def foo(x: Int) = x
那么T$class
将有:
<static> def foo($this: T, x: Int) = x
通过 mixin 组合某些类A
和某些 trait获得的类T
将生成一个特殊的桥接方法,该方法将调用转发到包含主体的静态方法。这样,方法的主体不会在混入的每个类中重复T
。这就是必须创建匿名类的原因 - 它必须为T
.
这是一个例子。当您通过混合组合创建新类时,例如调用new A with T
:
class A {
def bar = println("!")
}
trait T {
def foo(x: Int) = x
}
new A with T
编译器会将其大致重写为如下内容:
class A {
def bar = println("!")
}
<interface> T {
def foo(x: Int): Int
}
class T$class {
<static> def foo($this: T, x: Int) = x
}
class $anon extends A <implements> T {
// notice that `bar` is inherited, but `foo` is not
<bridge> def foo(x: Int) = T$class.foo(this, x)
}
new $anon
请注意,编译器实际上可以将调用点重写为foo
直接从调用点调用静态方法,而不是通过桥接方法。不这样做的原因是因为它不再支持子类型多态性。