【问题标题】:Why is an anonymous class created when mixing in a trait?为什么在混合特征时会创建一个匿名类?
【发布时间】:2012-06-30 12:20:54
【问题描述】:
scala> class A
defined class A

scala> trait B
defined trait B

创建A 类的对象给我们:

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

但是创建一个带有 B 特征的 A 类对象给了我们:

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

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

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

【问题讨论】:

    标签: class scala anonymous mixins


    【解决方案1】:

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

    创建匿名类的原因是该对象必须具有来自A 的所有方法和来自B 的所有方法的实现。在 JVM 字节码层面,这需要继承多个类,而 JVM 不支持多继承模型。

    为了模拟多重继承(或 mixin 组合,无论你如何称呼它),Scala 在创建 trait 时会执行以下操作:

    1. 如果 trait T 没有方法实现,它会创建一个接口来定义 trait 中的所有方法。
    2. 如果 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,以直接从调用点调用静态方法,而不是通过桥接方法。之所以不这样做是因为那样它就不再支持子类型化多态了。

    【讨论】:

    • 请注意,使用 -Xprint:mixin 参数调用 scalac 将显示 Scala 创建的确切结构。 -Xshow-phases 显示可与 -Xprint: 一起使用的其他阶段。
    【解决方案2】:

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

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

    【讨论】:

    • 谢谢。当专用类在性能方面更有意义时,匿名对象的数量是多少? 10、100、1000 或更多?
    • 它只会为代码中每次出现的 this 创建一个新类,而不是为此类的每个实例。因此,除非您在数百个代码行中有new A with B,这不太可能,否则应该没有问题。
    • @John - 我不知道。我个人已经有 10 个案例了。这是实例化A with B 的10个不同位置(正如@drexin 正确所说)。我不经常遇到这种情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-31
    • 1970-01-01
    • 2018-10-20
    • 2010-11-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多