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!
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
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:
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