【问题标题】:Are Scala constructor args duplicated?Scala 构造函数参数是否重复?
【发布时间】:2011-09-06 18:00:46
【问题描述】:

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

考虑以下 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 之前修改了“s”(“MY”+s)。

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

我在 Scala 2.8 和 2.9.1 上尝试过,结果相同。

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

谢谢!

【问题讨论】:

    标签: scala


    【解决方案1】:

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

    类展示示例:

    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
    

    【讨论】:

    • 感谢您的详细解释!
    • Y 中的类参数 s 会影响 X 中定义的 val。
    【解决方案2】:

    确实,参数 s 会影响超类参数。重要的一点是类参数(即主构造函数的参数)的范围是整个类。因此,在您的 Y.run 方法中,毫无疑问 s 指的是 Y.s。

    这意味着 s 必须在某个字段中保持活跃,正如 Rex 向您展示的那样,这正是正在发生的事情。

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

    • var => 生成字段、getter 和 setter
    • val => 生成字段和吸气剂
    • 如果需要,两个 => 都不创建字段(即方法中使用的参数),但没有 getter/setter

    【讨论】:

      猜你喜欢
      • 2013-09-08
      • 2013-03-16
      • 2013-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多