I have an object:
object A {
val init = println("Hello")
}
I use it in a trait:
trait SomeTratit {
val a = A.init
}
Then I use trait it in a class:
class SomeClass extends SomeTrait
When I instantiate SomeClass with new SomeClass
I expect to see "Hello"
in the console, but don't get it. Why?
Also I expect to see "Hello" only once while instantiating several objects, but don't see any "Hello" in console
An object initializer is an expression that describes the initialization of an Object . Objects consist of properties, which are used to describe an object. The values of object properties can either contain primitive data types or other objects.
Once you define a class, you can create objects from the class blueprint with the keyword new. Through the object you can use all functionalities of the defined class.
Instead of static keyword Scala has singleton object. A Singleton object is an object which defines a single object of a class. A singleton object provides an entry point to your program execution. If you do not create a singleton object in your program, then your code compile successfully but does not give output.
I don't know if this should be considered a bug, but here's HOW this happened.
If you look at the generated bytecode of object A
, it's like this:
public final class A$ {
public static final A$ MODULE$;
private final scala.runtime.BoxedUnit init;
public static {};
Code:
0: new #2 // class A$
3: invokespecial #12 // Method "<init>":()V
6: return
public void init();
Code:
0: return
private A$();
Code:
0: aload_0
1: invokespecial #16 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #18 // Field MODULE$:LA$;
8: aload_0
9: getstatic #23 // Field scala/Predef$.MODULE$:Lscala/Predef$;
12: ldc #25 // String Hello
14: invokevirtual #29 // Method scala/Predef$.println:(Ljava/lang/Object;)V
17: getstatic #34 // Field scala/runtime/BoxedUnit.UNIT:Lscala/runtime/BoxedUnit;
20: putfield #36 // Field init:Lscala/runtime/BoxedUnit;
23: return
}
You can find the println("Hello")
you expected in the constructor of A$
, but there's nothing in init()
. This is perfectly correct because it is you intention that println("Hello")
would not be executed every time when you called init()
, right?
However, the problem comes in SomeClass
:
public class SomeClass implements SomeTrait {
public void a();
Code:
0: return
public void SomeTrait$_setter_$a_$eq(scala.runtime.BoxedUnit);
Code:
0: return
public SomeClass();
Code:
0: aload_0
1: invokespecial #21 // Method java/lang/Object."<init>":()V
4: aload_0
5: invokestatic #27 // Method SomeTrait$class.$init$:(LSomeTrait;)V
8: return
}
What?! There's nothing in SomeClass.a()
, either! But think about it, it's also perfectly reasonable: why would I bother calling it since there's literally nothing in A$.init()
and it returns nothing (i.e. no field to be set)? Why not just optimize that out (Maybe Java did this optimization. Or Scala did. I don't know)? However, this optimization also erase the only appearance of A$
, which means no constructor will be called for A$
. That's why the Hello
never appeared.
However, if you change the code a little bit so that the bytecode of init()
will not be empty, like this:
object A {
val init = { println("Hello"); 1 }
}
which compiles to the following bytecode:
public int init();
Code:
0: aload_0
1: getfield #17 // Field init:I
4: ireturn
in which case you will find the bytecode for SomeClass.a()
like this:
public int a();
Code:
0: aload_0
1: getfield #15 // Field a:I
4: ireturn
where the field was set in SomeTrait$class
:
public abstract class SomeTrait$class {
public static void $init$(SomeTrait);
Code:
0: aload_0
1: getstatic #13 // Field A$.MODULE$:LA$;
4: invokevirtual #17 // Method A$.init:()I
7: invokeinterface #23, 2 // InterfaceMethod SomeTrait.SomeTrait$_setter_$a_$eq:(I)V
12: return
}
A$.init()
is called to set this field, so in this case you can expect Hello
to appear.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With