具有抽象变量和子构造函数的抽象 class;斯卡拉

Abstract class with abstract variables and child constructors; Scala

我有一个摘要 class 由另一个 class 扩展。

object WhosebugTest extends App
{
  new ChildFunction()
}

abstract class Function() {
  val a: Double
  val b: Double

  println(a, b)
}

class ChildFunction() extends Function() {
  val a = 0.02
  val b = 0.2
}

当我实例化 ChildFunction 时,它会为 ab 打印 0.0

这显然不是我想要的,甚至不是我期望的。

我发现解决此问题的唯一方法是在 ChildFunction lazy val a = 0.02 中说明。 这是正确的解决方案吗?

使 val 惰性起作用是因为它移动了写入字段 0.02 的点。要了解发生了什么,请阅读以下代码,这是 scalac 为您的示例生成的 java 字节代码。

需要注意的是,字段a和b是存储在子对象上的,它们的值0.02和0.2是在调用父对象的构造函数后才写入的。但是,在写入字段之前,在父级的构造函数中调用了 println。因此你的问题。

使 vals 延迟工作是因为在调用 a() 或 b() 时,将调用初始化代码.. 即父 class 现在将调用子代码class 在子 class 上设置字段。

public abstract class Function implements scala.ScalaObject {
  public abstract double a();

  public abstract double b();

  public Function();   // NB: calls a() and b() on the child class
    Code:
       0: aload_0       
       1: invokespecial #13                 // Method java/lang/Object."<init>":()V
       4: getstatic     #19                 // Field scala/Predef$.MODULE$:Lscala/Predef$;
       7: new           #21                 // class scala/Tuple2$mcDD$sp
      10: dup           
      11: aload_0       
      12: invokevirtual #25                 // Method a:()D
      15: aload_0       
      16: invokevirtual #27                 // Method b:()D
      19: invokespecial #30                 // Method scala/Tuple2$mcDD$sp."<init>":(DD)V
      22: invokevirtual #34                 // Method scala/Predef$.println:(Ljava/lang/Object;)V
      25: return        
}



public class ChildFunction extends Function implements scala.ScalaObject {
  public double a();
    Code:
       0: aload_0       
       1: getfield      #12                 // Field a:D
       4: dreturn       

  public double b();
    Code:
       0: aload_0       
       1: getfield      #14                 // Field b:D
       4: dreturn       

  public ChildFunction();    // NB invokes parent constructor BEFORE writing values to fields a and b.
    Code:
       0: aload_0       
       1: invokespecial #20                 // Method Function."<init>":()V
       4: aload_0       
       5: ldc2_w        #21                 // double 0.02d
       8: putfield      #12                 // Field a:D
      11: aload_0       
      12: ldc2_w        #23                 // double 0.2d
      15: putfield      #14                 // Field b:D
      18: return        
}

您可以 'fix' 通过使用 defs 而不是 lazy val 来解决这个问题(如下例)。或者更好的是,删除 println 并仅调用 a() 和 b() after ChildFunction 已完全构建。

object WhosebugTest extends App
{
  new ChildFunction()
}

abstract class Function() {
  def a: Double
  def b: Double

  println(a, b)
}

class ChildFunction() extends Function() {
  override def a = 0.02
  override def b = 0.2
}