【问题标题】:Overriding members and lazy val覆盖成员和惰性 val
【发布时间】:2013-08-04 20:06:13
【问题描述】:

我找到了this explanation,了解如何使用 lazy val 将覆盖的成员值传播到超类构造函数。不幸的是,这篇文章没有解释为什么会这样。

我知道非惰性值不能被分配两次,因此,在超级构造函数中没有可用的值,因为必须跳过超级类构造函数中的值分配才能不将变量锁定为另一个值。但是,println 语句(在超级构造函数中执行,即在分配新的惰性值之前)如何知道这个新值?我是否对执行顺序感到困惑?还是 println 只是在构造对象后才评估它的参数?

【问题讨论】:

    标签: scala constructor lazy-evaluation


    【解决方案1】:

    这很简单。您只需要注意所有字段和惰性字段都是 getter 方法。

    val getter 返回private[this] 字段的值。 private[this] 字段赋值位于主构造函数中:

    class Test {
      val field = 1
    }
    

    意思是这样的:

    class Test {
      private[this] val _field: Int = _
      def field = _field
    
      {  // constructor
        _field = 1
      }
    }
    

    所以在构造函数field返回默认值之前。

    lazy val 使用双重检查锁评估代码块,然后返回结果:

    class Test {
      lazy val field = 1
    }
    

    意思是这样的:

    class Test {
      private[this] val _field: Int = _
      @volatile private[this] val _flag: Boolean = _
    
      def field = {
        if (! _flag) {
          synchronized {
            if (! _flag) {
              _field = 1 // code block
              _flag = true
            }
          }
        }
    
        _field
      }
    }
    

    所以lazy val 没有构造函数,你会在构造函数之前和之后得到相同的结果。

    您可以使用scalac -Xprint:mixin test.scala 进行检查。

    【讨论】:

    • 这让它更清晰了。但是,我仍然想知道在将它设置为子构造函数中的实际值之前,getter 如何访问该字段的值。在我的理解中,println 代码行应该在 JVM 甚至到达设置新值的语句之前执行。这怎么可能? println 是否不需要解析 getter 并因此在执行时访问底层值?
    • @raphw 你是指哪个println
    • @raphw:在Use lazy vals. 示例中,所有println 都调用了2 个方法:x1x2x1 定义在 B 中,与构造函数无关。您可以将其定义为def,而不是vallazy valx2A 中定义,然后在C 中被覆盖。它也与构造函数无关。方法在构造函数中没有被覆盖,它只是被覆盖了。尝试将本例中的所有vallazy val 替换为def - 结果是一样的。
    • 好的,我现在明白了。我忽略了关键字如何将整个实现从构造函数更改为方法初始化。
    • 越想越不喜欢。我可以想到数百个场景,其中一些 lazy val = func() 会因为 this 监视器同步而造成死锁。然后,我发现 Scala 开发人员认为这是一个用户错误:issues.scala-lang.org/browse/… - 谢谢!
    猜你喜欢
    • 2013-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-27
    • 1970-01-01
    相关资源
    最近更新 更多