Why is an anonymous class created when mixed in a trait?

scala> class A defined class A scala> trait B defined trait B 

Creating an object of class A gives us:

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

But creating an object of class A with a modified attribute B gives us:

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

Here we have an anonymous class (scheduled by anon ). Why?

Is this because type A with B treated as a new type (and which was not previously defined with an identifier)?

+6
source share
2 answers

This is due not only to the fact that A with B should be considered as a new type. For a system of type Scala, it does not matter if a class exists corresponding to A with B An anonymous class is generated because it must contain modal methods for all methods in related attributes.

The reason an anonymous class is created is because the object must have implementations of all methods from A and all methods from B At the JVM bytecode level, this would guarantee the inheritance of several classes, and the multiple inheritance model was not supported on the JVM.

To simulate multiple inheritance (or the composition of a mixer, however you want to name it), Scala performs the following actions when creating a trait:

  • If the characteristic T does not have a method implementation, it creates an interface that defines all the methods in this attribute.
  • If the tag T has method implementations, it additionally creates the class T$class , which has a static method for each of the specific methods in T This static method has the same authority as its corresponding method in T , but its signature is changed to include the this parameter. If T :

     def foo(x: Int) = x 

then T$class will have:

 <static> def foo($this: T, x: Int) = x 

The class obtained by the mixin composition of some class A and some trait T will then have a special bridge method generated that forwards the call to the static method that contains the body. Thus, the body of the method is not duplicated in every class that mixes in T That is why an anonymous class must be created - it must have bridge methods defined for each method in T

Here is an example. When you create a new class by doing mixin composition, for example. calling new A with T :

 class A { def bar = println("!") } trait T { def foo(x: Int) = x } new A with T 

the compiler will rewrite it something like this:

 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 

Note that the compiler can indeed rewrite callites to foo to invoke static methods directly from callsite, rather than through a bridge method. The reason this is not the case is because then it will no longer support the politicism of the subtypes.

+13
source

Yes. While your type is still A with B , there must be a Java base class that implements both interfaces. There is nothing wrong with this, except that if you create objects this way hundreds of times, you will probably have hundreds of class files. In this case, you may need to create a dedicated class AB extends A with B , and then create an instance of new AB .

As a side note, you will find that you also cannot directly specify the symptoms, for example. new B will not work. Here you also need to create an explicit class. new B {} , which again leads to a synthetic ("anonymous") class.

+6
source

Source: https://habr.com/ru/post/919284/


All Articles