Why is a Scala companion object compiled into two classes (both Java and .NET compilers)?

object ScalaTrueRing { def rule = println("To rule them all") } 

this piece of code will be compiled into java byte code, if I decompile it, then the equivalent Java code is like this:

 public final class JavaTrueRing { public static final void rule() { ScalaTrueRing..MODULE$.rule(); } } /* */ public final class JavaTrueRing$ /* */ implements ScalaObject /* */ { /* */ public static final MODULE$; /* */ /* */ static /* */ { /* */ new (); /* */ } /* */ /* */ public void rule() /* */ { /* 11 */ Predef..MODULE$.println("To rule them all"); /* */ } /* */ /* */ private JavaTrueRing$() /* */ { /* 10 */ MODULE$ = this; /* */ } /* */ } 

it compiled into two classes, and if I use the Scala.net compiler, it will be compiled into MSIL code, and the equivalent C # code looks like this:

 public sealed class ScalaTrueRing { public static void rule() { ScalaTrueRing$.MODULE$.rule(); } } [Symtab] public sealed class ScalaTrueRing$ : ScalaObject { public static ScalaTrueRing$ MODULE$; public override void rule() { Predef$.MODULE$.println("To rule them all"); } private ScalaTrueRing$() { ScalaTrueRing$.MODULE$ = this; } static ScalaTrueRing$() { new ScalaTrueRing$(); } } 

It is also compiled into two classes.

Why do Scala compilers (one for Java and one for .NET) do this? Why doesn't he just call the println method in the static rules method?

+6
source share
3 answers

It is important to understand that in scala, an object actually a first-class citizen: it is an actual instance that can be transferred like any other object. Example:

 trait Greetings { def hello() { println("hello") } def bye() { println("bye") } } object FrenchGreetings extends Greetings { override def hello() { println("bonjour") } override def bye() { println("au revoir") } } def doSomething( greetings: Greetings ) { greetings.hello() println("... doing some work ...") greetings.bye() } doSomething( FrenchGreetings ) 

Unlike static methods, our singleton object has a full polymorphic beheviour. doSomething will actually call our overridden hello and bye methods, not standard implementations:

 bonjour ... doing some work ... au revoir 

Thus, the implementation of object must necessarily be the proper class. But to interact with java, the compiler also generates static methods that are simply passed to the unique instance ( MODULE$ ) of the class (see JavaTrueRing.rule ()). Thus, a java program can access the methods of a single object as a regular static method. You may now ask why scala does not put static forwarding methods in the same class as instance methods. This will give us something like:

 public final class JavaTrueRing implements ScalaObject { public static final MODULE$; static { new JavaTrueRing(); } public void rule() { Predef.MODULE$.println("To rule them all"); } private JavaTrueRing() { MODULE$ = this; } // Forwarders public static final void rule() { MODULE$.rule(); } } 

I believe that the main reason why this cannot be so simple is that in the JVM you cannot have an instance method and a static wth method of the same signature in the same class. However, there may be other reasons.

+8
source

To paraphrase "Programming in Scala" - because the scala object is a companion object (singleton object) more than just a carrier of static methods. Being an instance of another Java class, it allows the developer to extend singleton objects and mixing objects. This cannot be done with static methods.

+3
source

This blog post, β€œA Look at How Scala Compiles in Java” should answer your question

Typically, ClassName $ .class is the result of inner classes - Scala is obviously a little different.

+1
source

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


All Articles