Почему объект Scala companion компилируется в два класса (оба Java and.NET компиляторы)?
object ScalaTrueRing {
def rule = println("To rule them all")
}
Этот фрагмент кода будет скомпилирован в байтовый код java, если я декомпилирую его, то эквивалентный код Java будет похож на этот:
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;
/* */ }
/* */ }
Он скомпилирован в два класса, и если я использую Scala.net компилятор, он будет скомпилирован в код MSIL, и эквивалентный код C# выглядит следующим образом:
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$();
}
}
Он также скомпилирован в два класса.
Почему компиляторы Scala (один для Java и один для .NET) делают это? Почему бы ему просто не вызвать метод println в статике метод правления?
3 ответа:
Важно понимать, что в scala
object
На самом деле является гражданином первого класса: это фактический экземпляр, который может передаваться как любой другой объект. Например: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 )
В отличие от статических методов, наш синглетный объект имеет полное полиморфное поведение.
doSomething
действительно вызовет наши переопределенныеhello
иbye
методы, а не реализации по умолчанию:Таким образом, реализацияbonjour ... doing some work ... au revoir
object
обязательно должна быть правильным классом. Но для совместимости с java, компилятор также генерирует статические методы, которые просто перенаправляют уникальный экземпляр (MODULE$
) класса (см. JavaTrueRing.правило()). Таким образом, программа java может обращаться к методам синглетного объекта как к обычному статическому методу. Теперь вы можете спросить, почему scala не помещает форвардеры статических методов в тот же класс, что и методы экземпляра. Это дало бы нам что-то вроде:Я считаю, что главная причина, по которой это не может быть так просто, заключается в том, что в СПМ вы не можете иметь в одном и том же классе метод экземпляра и статический метод с одной и той же сигнатурой. Впрочем, могут быть и другие причины.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(); } }
Перефразируя из "программирования в Scala" - потому что объект-компаньон scala (singleton object) - это больше, чем просто держатель статических методов. Будучи экземпляром другого класса Java, он позволяет разработчику расширять одноэлементные объекты и смешивать черты. Это невозможно сделать с помощью статических методов.
Эта запись в блоге "a Look at How Scala Compiles to Java" должна ответить на ваш вопрос
Обычно Имя Класса$.класс - это результат внутренних классов-Scala, очевидно, немного отличается.