Синглтоны как синтетические классы в Scala?
Я читаю Программирование в Scala, и я не понимаю следующее предложение (pdf стр .112):
Каждый объект singleton реализуется как экземпляр синтетического класса, на который ссылается статическая переменная, поэтому они имеют ту же семантику инициализации, что и статическая статистика Java.
Означает ли это, что если у меня есть singleton FooBar в Scala, компилятор создаст класс с именем FooBar $?
Также, что означает автор, ссылаясь на статическую переменную? Есть ли скрытая статическая переменная где-то содержащая ссылку на некоторый класс FooBar $?
Я ценю любую помощь здесь.
Ответы
Ответ 1
Более точно описана глава 31 того же "Программирование в Scala":
Java не имеет точного эквивалента одиночному объекту, но имеет статические методы.
Трансляция объектов singleton с помощью Scala использует комбинацию статических и экземплярных методов. Для каждого объекта Scala singleton, компилятор создаст класс Java для объекта со знаком доллара, добавленным в конец.
Для одноэлементного объекта с именем App
компилятор создает класс Java с именем App$
.
Этот класс имеет все методы и поля объекта Scala singleton.
Класс Java также имеет одно статическое поле с именем MODULE$
, чтобы удерживать один экземпляр класса, созданного во время выполнения.
В качестве полного примера предположим, что вы скомпилируете следующий одноэлементный объект:
object App {
def main(args: Array[String]) {
println("Hello, world!")
}
}
Scala создаст класс Java App $со следующими полями и методами:
$ javap App$
public final class App$ extends java.lang.Object
implements scala.ScalaObject{
public static final App$ MODULE$;
public static {};
public App$();
public void main(java.lang.String[]);
public int $tag();
}
Это перевод для общего случая.
Ответ 2
Вы в основном правы.
Если у вас синглтон
object Singleton {
def method = "Method result"
}
то компиляция дает вам
Singleton.class
Singleton$.class
и для байт-кода, который вы найдете, сначала для Singleton
:
public final class Singleton extends java.lang.Object{
public static final java.lang.String method();
Signature: ()Ljava/lang/String;
Code:
0: getstatic #11; //Field Singleton$.MODULE$:LSingleton$;
3: invokevirtual #13; //Method Singleton$.method:()Ljava/lang/String;
6: areturn
}
то есть публичный статический метод для каждого метода класса, который ссылается на то, что называется Singleton$.MODULE$
, и в Singleton$
:
public final class Singleton$ extends java.lang.Object implements scala.ScalaObject{
public static final Singleton$ MODULE$;
Signature: LSingleton$;
public static {};
Signature: ()V
Code:
0: new #9; //class Singleton$
3: invokespecial #12; //Method "<init>":()V
6: return
public java.lang.String method();
Signature: ()Ljava/lang/String;
Code:
0: ldc #16; //String Method result
2: areturn
private Singleton$();
Signature: ()V
Code:
0: aload_0
1: invokespecial #20; //Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #22; //Field MODULE$:LSingleton$;
8: return
}
Где вы видите, что MODULE$
- это то, что содержит экземпляр Singleton$
, а method
- обычный метод.
Итак, все, что ему действительно нужно: создайте Singleton$
со статическим полем MODULE$
, чтобы сохранить уникальный экземпляр самого себя, заполнить это поле, а затем создать Singleton
со статическими методами, которые пересылают все статические вызовы для соответствующих методов из Singleton$
.