【问题标题】:Build Immutable List though Inheritance通过继承构建不可变列表
【发布时间】:2012-09-16 06:36:35
【问题描述】:

是否可以通过 Scala 中的继承来构建不可变列表?我有一个创建 MenuBar 的基本特征。 MenuBar 将具有标准标题:文件、编辑、视图/窗口、帮助等。我希望继承特征能够将子项添加到菜单标题。我希望动态创建菜单,因此将有一个函数列表,当打开菜单以创建子树时将调用这些函数。由于函数列表将在编译时知道,如果它可以是一个不可变列表会很好。

Scala 中是否有任何方法可以通过构造函数/初始化层次结构创建不可变列表?如果不是在 Scala 中,是否有任何语言提供此功能?

用一个使用可变列表的简单示例来说明问题。 TrA 和 TrB 是相互独立编写的:

trait Base
{
  val list = scala.collection.mutable.LinkedList[String]()
}
trait TrA extends Base
{
  list += "A"
}
trait TrB extends Base
{
  list += "B"
}

val ab = new TrA with TrB {}

由于 List 的内容在编译时是已知的,是否有使其成为 val 不可变列表? 当然,任何可变集合都可以通过 def 调用作为不可变集合公开。

以下将编译:

trait Base
{ val list: List[String] = Nil }

trait TrA extends Base
{ val list = "A" :: super.list }

trait TrB extends Base
{ val list = "B" :: super.list }

val ab = new TrA with TrB {}

但它不能在不抛出空异常的情况下被初始化。使 val list 变得懒惰也无济于事,因此需要 0__ 的解决方案。

【问题讨论】:

  • 您能否提供一个示例(伪)代码来显示您想要实现的目标?
  • @PetrPudlák 希望添加的代码可以解释问题。

标签: scala immutability multiple-inheritance scala-collections


【解决方案1】:

这与我记得的一个较早的问题非常相似;我再也找不到它了,所以我尝试重建方法:

trait Base {
  protected def contribute : List[String] = Nil

  val list = contribute
}

trait TrA extends Base {
   override protected def contribute = "A" :: super.contribute
}

trait TrB extends Base {
   override protected def contribute = "B" :: super.contribute
}

val x = new TrA with TrB {}
x.list  // List(B, A)
val y = new TrB with TrA {}
y.list  // List(A, B)

【讨论】:

  • 聪明。我不知道这是可能的。有趣的是with 的顺序是如何决定继承顺序的。我在this article 中找到了一个解释,他们在多重继承和方法合作中写道:实际上,Scala 特征可以在它们覆盖相同的方法时进行组合,并且组合的顺序决定了超级调用的结果模式:这意味着 Scala 特征基本上具有 Python 混合的所有复杂性,我宁愿避免。
  • 另外 Wikipedia 的文章 Multiple inheritance / Diamond problem 讨论了 Scala 如何解决多重继承问题。
  • 初读时我错过了,这个解决方案是多么出色。至少在我们获得具有适当多重继承的 Scala 虚拟机之前。
【解决方案2】:

我会隐藏列表的可变部分,以便只有 Base 的后代可以添加到它(除此之外):

trait Base {
  private val theList = scala.collection.mutable.LinkedList[String]()

  protected def add(item: String) {
    theList += item;
  }

  // return only an immutable copy of the list
  def list: Seq[String] = theList.toSeq; // or .toList, .toSet, etc.
}

trait TrA extends Base {
  add("Apple")
}

trait TrB extends Base {
  add("Orange")
}

虽然行为不端的后代仍然可以稍后添加一些项目,但从外部修改列表是不可能的。也许可以将def list 替换为lazy val list 以获得一些效率,但您必须确保它没有在任何构造函数中调用。

【讨论】:

    猜你喜欢
    • 2014-01-29
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多