Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are Scala constructor args duplicated?

Tags:

scala

I just stumbled about some situation that seems weird to me. I might very well be missing the obvious here- anyway, please help me out.

Consider the following Scala repl script:

scala> class X(val s: String) { def run=println("(X): "+s) }
defined class X

scala> class Y(s: String) extends X("MY "+s) { override def run=println("(Y): "+s) }
defined class Y

scala> new Y("fish").run
(Y): fish

In the script I'm defining a class X with a class attribute "val s". Then I define a class Y that is supposed to take one constructor argument and pass it to X- which it does. To show the difference I modify "s" before I give it to X ("MY "+s).

Finally I create a new Y and call "run". This prints "fish" to the console so obviously the attribute "s" of class "X" has been shadowed by a new attribute "s" that I created in "Y".

I tried this with Scala 2.8 and 2.9.1 with the same result.

Is this supposed to be this way? What do I do if I just want to pass constructor arguments from my class down to a superclass and do not want to store the parameter myself inside the child class? What is the common practice here?

Thanks!

like image 944
Torsten Uhlmann Avatar asked Sep 06 '11 18:09

Torsten Uhlmann


2 Answers

If you do not use the parameter except in the constructor of the subclass, the parameter will not be stored. If you need to refer to the parent parameter not the constructor parameter, use a different variable name.

Classes showing examples:

class X(val s: String) { def run=println("(X): "+s) }
class Y(s: String) extends X("MY "+s) { override def run=println("(Y): "+s) }
class Z(s0: String) extends X("MY "+s0) { override def run=println("(Z): "+s) }

Bytecode showing lack of storage (just the constructors):

// Note putfield to store s
public X(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #11; //Field s:Ljava/lang/String;
   5:   aload_0
   6:   invokespecial   #43; //Method java/lang/Object."<init>":()V
   9:   return

// Note putfield to store new s (then eventually calls X's constructor)
public Y(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield    #29; //Field s:Ljava/lang/String;
   5:   aload_0
   6:   new #16; //class scala/collection/mutable/StringBuilder
   9:   dup
   10:  invokespecial   #19; //Method scala/collection/mutable/StringBuilder."<init>":()V
   13:  ldc #40; //String MY 
   15:  invokevirtual   #25; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   18:  aload_1
   19:  invokevirtual   #25; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   22:  invokevirtual   #33; //Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;
   25:  invokespecial   #44; //Method X."<init>":(Ljava/lang/String;)V
   28:  return

// Note - no putfield!
public Z(java.lang.String);
  Code:
   0:   aload_0
   1:   new #14; //class scala/collection/mutable/StringBuilder
   4:   dup
   5:   invokespecial   #17; //Method scala/collection/mutable/StringBuilder."<init>":()V
   8:   ldc #39; //String MY 
   10:  invokevirtual   #23; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   13:  aload_1
   14:  invokevirtual   #23; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   17:  invokevirtual   #32; //Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;
   20:  invokespecial   #43; //Method X."<init>":(Ljava/lang/String;)V
   23:  return
like image 164
Rex Kerr Avatar answered Oct 06 '22 12:10

Rex Kerr


Indeed, the parameter s shadows the superclass parameter. The important point is that the scope of a class parameter (i.e. a parameter of the primary constructor) is the entire class. So, in your Y.run method, there is no question that s refers to Y.s.

That means that s must have been kept alive in a field, and as Rex has shown you, that's exactly what is going on.

For a primary constructor parameter, there are three choices:

  • var => makes field, getter, and setter
  • val => makes field and getter
  • neither => makes field if necessary (i.e. parameter used in methods), but no getter/setter
like image 31
cayhorstmann Avatar answered Oct 06 '22 13:10

cayhorstmann