6
scala> class A
defined class A

scala> trait B
defined trait B

创建一个类对象A给了我们:

scala> new A
res4: A = A@11ea3fc

但是创建一个混合A了 trait的类对象B给了我们:

scala> new A with B
res3: A with B = $anon$1@172aa3f

这里我们有一个匿名类(用 暗示anon)。为什么 ?

这是因为该类型A with B被视为一种新类型(并且之前没有使用标识符定义)吗?

4

2 回答 2

13

这不仅是因为A with B必须被视为一种新类型。对于 Scala 类型系统,是否存在与A with B. 生成了一个匿名类,因为它必须包含已混入的特征中所有方法的桥接方法。

创建匿名类的原因是该对象必须具有 from 的所有方法和 from 的A所有方法的实现B。在 JVM 字节码级别,这将保证继承多个类,并且 JVM 不支持多继承模型。

为了模拟多重继承(或 mixin 组合,无论你怎么称呼它),Scala 在你创建一个 trait 时会做以下事情:

  1. 如果 traitT没有方法实现,它会创建一个接口来定义 trait 中的所有方法。
  2. 如果 traitT有方法实现,它会另外创建一个类,该类T$classT. 此静态方法与 中的相应方法具有相同的主体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直接从调用点调用静态方法,而不是通过桥接方法。不这样做的原因是因为它不再支持子类型多态性。

于 2012-06-28T23:26:32.543 回答
6

是的。虽然您的类型仍然是A with B,但需要有一个实现这两个接口的底层 Java 类。这并没有什么问题,只是如果你以这种方式创建对象数百次,你可能会拥有数百个类文件。在这种情况下,您可能需要创建一个专用class AB extends A with B然后实例化new AB.

作为旁注,你会发现你也不能直接实例化特征,例如new B不会工作。您还需要在这里创建一个显式类,例如new B {},再次生成一个合成(“匿名”)类。

于 2012-06-28T22:38:58.167 回答