这不仅仅是因为A with B 必须被视为一种新类型。对于 Scala 类型系统,是否存在对应于A with B 的类并不直接重要。生成了一个匿名类,因为它必须包含 bridge 方法,用于已混入的特征中的所有方法。
创建匿名类的原因是该对象必须具有来自A 的所有方法和来自B 的所有方法的实现。在 JVM 字节码层面,这需要继承多个类,而 JVM 不支持多继承模型。
为了模拟多重继承(或 mixin 组合,无论你如何称呼它),Scala 在创建 trait 时会执行以下操作:
- 如果 trait
T 没有方法实现,它会创建一个接口来定义 trait 中的所有方法。
-
如果 trait T 有方法实现,它会另外创建一个类 T$class,它为T 中的每个具体方法提供一个静态方法。此静态方法与T 中的相应方法具有相同的主体,但其签名已更改为包含this 参数。如果T 有:
def foo(x: Int) = x
那么T$class 将有:
<static> def foo($this: T, x: Int) = x
通过mixin组合一些类A和一些特征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,以直接从调用点调用静态方法,而不是通过桥接方法。之所以不这样做是因为那样它就不再支持子类型化多态了。