Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala object initialization

Tags:

scala

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

like image 900
maks Avatar asked Jun 14 '14 15:06

maks


People also ask

What is object initialization?

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.

How do I create a new object in Scala?

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.

Is Scala object a singleton?

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.


1 Answers

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.

like image 114
lastland Avatar answered Oct 14 '22 13:10

lastland