2011-09-06 55 views
5

我只是偶然发现了一些对我来说很奇怪的情况。无论如何,我可能很想念这个明显的问题,请帮助我。斯卡拉构造函数参数是否重复?

考虑下面的斯卡拉REPL脚本:

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 

在脚本中,我定义一个类属性“VAL的”一类X。 然后我定义了一个类Y,它应该接受一个构造函数参数并将它传递给它所做的X-。为了显示不同之处,我修改了“s”,然后将其赋予X(“我的”+ s)。

最后我创建一个新的Y并调用“run”。这将“fish”打印到控制台,因此显然类“X”的属性“s”已被我在“Y”中创建的新属性“s”遮蔽。

我试过这与Scala 2.8和2.9.1具有相同的结果。

这应该是这样吗?如果我只想将构造函数参数从我的类传递到超类,而不想将自己存储在子类中,该怎么办?这里常见的做法是什么?

谢谢!

回答

8

如果除了在子类的构造函数中使用参数,参数将不会被存储。如果您需要引用父参数而不是构造函数参数,请使用不同的变量名称。

类表示的例子:

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) } 

字节码表示缺乏存储的(只是构造函数):

// 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 
+0

谢谢你的详细解释! –

+0

Y中的类参数隐藏了X中定义的val。 – mkneissl

1

实际上,参数s阴影超类参数。重要的一点是,类参数的范围(即主构造函数的参数)是整个类的。所以,在你的Y.run方法中,s不会引用Y.s.

这意味着s必须在一个领域保持活力,正如Rex向您展示的那样,这正是发生的。

对于主构造符参数,有三个选择:

  • VAR =>使得场,吸气剂,和setter
  • VAL =>使得字段和getter
  • 既不=>使得场如果必要的(即方法中使用的参数),但没有吸气器/设置器