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.
println("Hello")with{ println("Hello"); 1 }, so it looks like an optimization forUnitgone awry.A.initin the body of the trait, it works but compiler emits warning