Scala构造函数args是否重复?

时间:2011-09-06 18:00:47

标签: scala

我只是偶然发现了一些对我来说很奇怪的情况。我很可能会错过这里明显的 - 无论如何,请帮助我。

考虑以下Scala 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 s”的类X. 然后我定义一个类Y,它应该采用一个构造函数参数并将其传递给它所做的X-。为了显示差异,我在将其赋予X(“MY”+ s)之前修改“s”。

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

我用Scala 2.8和2.9.1尝试了相同的结果。

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

谢谢!

2 个答案:

答案 0 :(得分:8)

如果不使用除子类构造函数之外的参数,则不会存储该参数。如果需要引用parent参数而不是构造函数参数,请使用其他变量名。

显示示例的类:

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

答案 1 :(得分:1)

实际上,参数s会影响超类参数。重要的是,类参数的范围(即主构造函数的参数)是整个类。所以,在你的Y.run方法中,毫无疑问s指的是Y.s.

这意味着s必须在一个领域保持活力,正如雷克斯向你展示的那样,这正是发生的事情。

对于主构造函数参数,有三种选择:

  • var =&gt;制作字段,getter和setter
  • val =&gt;制造田野和吸气剂
  • both =&gt;必要时创建字段(即方法中使用的参数),但没有getter / setter