Class 为什么在混合特征时会创建匿名类?

Class 为什么在混合特征时会创建匿名类?,class,scala,anonymous,mixins,Class,Scala,Anonymous,Mixins,创建类A的对象将为我们提供: scala> class A defined class A scala> trait B defined trait B scala> new A res4: A = A@11ea3fc 但是,创建一个包含traitB的类A对象会给我们带来: scala> class A defined class A scala> trait B defined trait B scala> new A res4: A = A@11

创建类
A
的对象将为我们提供:

scala> class A
defined class A

scala> trait B
defined trait B
scala> new A
res4: A = A@11ea3fc
但是,创建一个包含trait
B
的类
A
对象会给我们带来:

scala> class A
defined class A

scala> trait B
defined trait B
scala> new A
res4: A = A@11ea3fc
这里我们有一个匿名类(由
anon
暗示)。为什么?

这是因为带B的类型
A被视为一个新类型(以前没有用标识符定义过)?

是。虽然您的类型仍然是
A和B
,但需要有一个实现这两个接口的底层Java类。这没有什么错,只是如果您以这种方式创建对象数百次,您可能会有数百个类文件。在这种情况下,您可能需要创建一个专用的
类AB,用B
扩展a,然后实例化
新AB


作为补充说明,您会发现您也无法直接实例化trait,例如,
newb
将不起作用。您也需要在这里创建一个显式类,例如,
newb{}
,再次生成一个合成(“匿名”)类。

这不仅仅是因为带B的
a必须被视为一个新类型。对于Scala类型的系统,是否存在对应于
a和B的类并不直接重要。生成匿名类是因为它必须包含混合在traits中的所有方法的桥接方法

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

为了模拟多重继承(或mixin组合,不管您如何称呼它),Scala在创建特征时会执行以下操作:

  • 如果trait
    T
    没有方法实现,它将创建一个定义trait中所有方法的接口
  • 如果trait
    T
    有方法实现,它会另外创建一个类
    T$class
    ,该类对
    T
    中的每个具体方法都有一个静态方法。此静态方法的主体与
    T
    中对应的方法相同,但其签名已更改为包含
    This
    参数。如果
    T
    had:

    scala> new A with B
    res3: A with B = $anon$1@172aa3f
    
  • 然后,
    T$class
    将具有:

    def foo(x: Int) = x
    
    编译器将大致将其重写为以下内容:

    class A {
      def bar = println("!")
    }
    
    trait T {
      def foo(x: Int) = x
    }
    
    new A with T
    
    A类{
    def bar=println(“!”)
    }
    T{
    def foo(x:Int):Int
    }
    等级T$等级{
    def foo($this:T,x:Int)=x
    }
    类$anon扩展了一个T{
    //请注意,“bar”是继承的,但“foo”不是
    def foo(x:Int)=T$class.foo(this,x)
    }
    新$anon
    

    注意,编译器实际上可以将调用站点重写为
    foo
    ,直接从调用站点调用静态方法,而不是通过桥接方法。之所以不这样做,是因为这样它就不再支持子类型多态性了。

    谢谢。当专用类在性能方面更有意义时,匿名对象的数量是多少?10、100、1000或更多?它只会为代码中出现的每一个此类创建一个新类,而不是为该类的每个实例创建一个新类。所以,除非你在数百行代码中有
    新的A和B
    ,这是不可能的,否则应该不会有问题。@John-我不知道。我个人已经有了10个案例的课程;这是10个不同的点,
    A和B
    被实例化(正如@drexin正确地说的)。我不经常遇到这种情况。请注意,使用
    -Xprint:mixin
    参数调用
    scalac
    ,将显示Scala创建的确切结构
    -Xshow阶段
    显示可与
    -Xprint:
    一起使用的其他阶段。