1

I have an abstract class which is extended by another class.

object StackOverflowTest extends App
{
  new ChildFunction()
}

abstract class Function() {
  val a: Double
  val b: Double

  println(a, b)
}

class ChildFunction() extends Function() {
  val a = 0.02
  val b = 0.2
}

When I instantiate ChildFunction, it prints 0.0 for a and b.

That is obviously not the behaviour I want, or even expected.

The only way I found of fixing this issue is stating in ChildFunction lazy val a = 0.02. Is this the proper solution?

2
  • 1
    It would help us if your example was complete so that we could run it. What is Terminal and NeuronalFunction, are they even necessary for the purposes of reproducing the problem? Commented Mar 26, 2015 at 14:00
  • @ChrisK You are absolutely right. I cleaned up the example. Commented Mar 26, 2015 at 14:05

1 Answer 1

4

Making the val lazy works because it moves the point where 0.02 is written into the field. To understand what is happening, have a read of the following code, which is the java byte codes that scalac produces for your example.

The thing to notice is that the fields a and b are stored on the child object, and that their values 0.02 and 0.2 are not written until after the constructor of the parent is called. However the println is being invoked in the constructor of the parent, before the fields have been written to. Thus your problem.

Making the vals lazy works because at the point when a() or b() is called, the initialisation code will be called.. that is the parent class will now be calling code on the child class to setup fields on the child class.

public abstract class Function implements scala.ScalaObject {
  public abstract double a();

  public abstract double b();

  public Function();   // NB: calls a() and b() on the child class
    Code:
       0: aload_0       
       1: invokespecial #13                 // Method java/lang/Object."<init>":()V
       4: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       7: new           #21                 // class scala/Tuple2$mcDD$sp
      10: dup           
      11: aload_0       
      12: invokevirtual #25                 // Method a:()D
      15: aload_0       
      16: invokevirtual #27                 // Method b:()D
      19: invokespecial #30                 // Method scala/Tuple2$mcDD$sp."<init>":(DD)V
      22: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      25: return        
}



public class ChildFunction extends Function implements scala.ScalaObject {
  public double a();
    Code:
       0: aload_0       
       1: getfield      #12                 // Field a:D
       4: dreturn       

  public double b();
    Code:
       0: aload_0       
       1: getfield      #14                 // Field b:D
       4: dreturn       

  public ChildFunction();    // NB invokes parent constructor BEFORE writing values to fields a and b.
    Code:
       0: aload_0       
       1: invokespecial #20                 // Method Function."<init>":()V
       4: aload_0       
       5: ldc2_w        #21                 // double 0.02d
       8: putfield      #12                 // Field a:D
      11: aload_0       
      12: ldc2_w        #23                 // double 0.2d
      15: putfield      #14                 // Field b:D
      18: return        
}

You can 'fix' this issue by using defs instead of lazy val (example below). Or better yet, remove the println and only invoke a() and b() after ChildFunction has been fully constructed.

object StackOverflowTest extends App
{
  new ChildFunction()
}

abstract class Function() {
  def a: Double
  def b: Double

  println(a, b)
}

class ChildFunction() extends Function() {
  override def a = 0.02
  override def b = 0.2
}
Sign up to request clarification or add additional context in comments.

2 Comments

I had a clue that something like this was happening. This can lead to many bugs. Is there a way of making sure that a and b are loaded without making them lazy?
By using defs, the compiler will hardcode the values within the methods. Otherwise I would avoid placing println within the constructor of the parent and instead call it after the ChildFunction has been fully initialised..

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.