当前位置: 首页 > 知识库问答 >
问题:

在trait中创建抽象val的适当延迟初始化

魏鸿哲
2023-03-14

考虑以下情况:

trait A {
    protected val mydata = ???

    def f(args) = ??? //uses mydata
}

class B
class C

class D(arg1: String) extends B with A {
    override val mydata = ??? /// some calculation based on arg1
}

class E(arg1: String) extends C with A{
    override val mydata = ??? /// some calculation based on arg1
}

A必须是一个特征,因为它被不同的无关类使用。问题是如何实现mydata的定义。

标准方法(在许多地方建议)是将mydata定义为def并在子级中重写它。但是,如果f假设mydata从不更改,那么当一些子级使用在调用之间更改的函数而不是val进行扩展时,它可能会导致问题。

另一种方法是:

trait A {
  protected val mydata = g
  protected def g()
}

这个问题(除了添加另一个函数外)是,如果g依赖于子对象中的构造变量,那么这些变量必须成为子对象的成员(例如,如果数据很大并且在构造中给定,这可能是一个问题):

 class D(arg1: Seq[String]) {
     def g() = ??? // some operation on arg1
 }

如果我将val作为抽象形式保留在trait中,我可以处理这里发现的问题)。

我正在寻找的是一种在子级中定义val值的方法,确保它是val并且无需为后期计算保存数据。类似于在java中如何定义最终val并将其填充到构造函数中

共有1个答案

周洋
2023-03-14

标准方法(在许多地方建议将我的数据定义为def,并在子对象中覆盖它……如果我将val保留在trait中作为抽象,我可以解决这里发现的问题)。

这是一种常见的误解,也体现在对相关问题的公认答案中。该问题正在实现为您无论如何都需要的val。覆盖一个具体的val只会使情况变得更糟:抽象的val至少可以由一个懒惰的val来实现。对您来说,避免该问题的唯一方法是确保在初始化之前,不会直接或间接地在a的构造函数或其子类型中访问mydata。在f中使用它是安全的(前提是构造函数中没有调用f,这将是对mydata的间接访问)。

如果你能保证这一要求,那么

trait A {
    protected val mydata
    def f(args) = ??? //uses mydata
}

class D(arg1: String) extends B with A {
    override val mydata = ??? /// some calculation based on arg1
}

class E(arg1: String) extends C with A{
    override val mydata = ??? /// some calculation based on arg1
}

正是正确的定义。

如果您不能,那么尽管存在缺点,但您必须使用上一个解决方案,但mydata需要惰性以避免类似的初始化顺序问题,这本身就会产生相同的缺点。

 类似资料:
  • 7.4.4 延迟初始化的bean 默认情况下,ApplicationContext实现在初始化过程中随即创建和配置所有单例bean。一般来说,这种预实例化是可取的,因为可以立即发现配置或周围环境中的错误,而不是在几个小时甚至几天以后。当这种行为不可取时,可以通过将bean定义标记为延迟初始化来阻止预实例化。延迟初始化的bean告诉IoC容器,当bean首次被请求时而不是在启动时创建一个实例。 在X

  • 有人以前有过类似的问题吗? 如何声明init()的默认变量值? 下面是我的代码示例, 然后在下面抛出异常: 起因:科特林。UninitializedPropertyAccessException:late init属性emailDir尚未初始化 任何解决方案都可以共享?

  • 我有maploader,它使用索引进行查询 > maploader 主 这个工作绝对精细的地图不会加载,直到我第一次接触地图。 但是当我为地图添加索引的时候,地图就会被加载,而不管是不是触摸地图。在Hazelcast文档中,MapStoreConfig类中的InitialLoadMode配置参数有两个值:LAZY和eager。如果InitialLoadMode设置为LAZY,则在映射创建期间不加载

  • 问题内容: 我做了一个二十一点游戏,我想让AI播放器在两张牌之间暂停一下。我尝试仅使用Thread.sleep(x)进行尝试,但这会使冻结,直到AI播放器拿走了他所有的卡。我知道Swing不是线程安全的,所以我看了Timers,但是我不明白如何使用它。这是我当前的代码: BTW,hit(); 方法更新GUI。 问题答案: 好吧,下面的代码显示了一个带有JTextArea和JButton的JFram

  • 问题内容: 我想暂时暂停我的应用。换句话说,我希望我的应用执行代码,但是在某个时候暂停4秒钟,然后继续执行其余的代码。我怎样才能做到这一点? 我正在使用Swift。 问题答案: 如果要从UI线程调用该方法,则可以考虑使用或调度计时器,而不是进行睡眠(这会锁定您的程序)。 但是,如果您确实需要延迟当前线程: 这使用UNIX中的功能。

  • 问题内容: 我想延迟控制器的初始化,直到从服务器收到必要的数据为止。 我找到了针对Angular1.0.1的解决方案:延迟AngularJS路由更改,直到加载模型以防止闪烁,但无法使其与Angular1.1.0一起使用 模板 ​ JavaScript http://jsfiddle.net/dTJ9N/1/ 问题答案: 由于$ http返回了promise,因此创建自己的deferred仅在htt