继承保持类型的特性

4

我对Scala还比较陌生,它的复杂类型系统让我有些困惑。

我试图解决以下问题。我想设计作为可变双向链表成员的类(以及更复杂的数据结构,如堆)。我的目标当然是能够在常数时间内从数据结构中删除对象。

我设计了以下特质:

trait DLLMember {
  var next: DLLMember = this
  var prev: DLLMember = this

  ...
}

这里有几个额外的方法可以添加和从列表中删除对象。

问题在于,当我在我的实际类中时:

class IntInDL(val v: Int) extends DLLMember

当解析我的列表时,IntInDL.next会返回一个DLLMember类型而不是IntInDL,我必须强制转换才能检索值:这不好...

有没有一种方法可以利用Scala的类型系统来保持我的工作类型安全?

1个回答

6

这个方法有点绕,但以下步骤应该可以解决问题:

trait DLLMember[T >: Null <: DLLMember[T]] { self : T =>
  var next: T = this
  var prev: T = this
  ...
}

class IntInDL(val v: Int) extends DLLMember[IntInDL]

var i = new IntInDL(3)
println(i.next.v) //prints 3
i.next = null //is valid
i.next = new IntInDL(1) //is valid

基本上,这里的意思是你用self : T => 表示类型参数T 必须是应用此 trait 的类的超类。当你在 IntInDL 中使用 trait 时,它现在已知nextprev 变量必须是 IntInDL的某个子类型,因为你提供了它作为类型参数。因此,您可以直接使用它们的成员,而无需进行强制转换。
如果您提供了一些不属于继承结构的其他任意类型,例如class IntInDL(val v: Int) extends DLLMember[String],编译将会失败。

它的工作很好,但是你如何允许next和prev为空值?(除了将其包装在Options中之外) - scand1sk
@scand1sk,您需要在类型参数上添加一个额外的限制,我已经编辑了答案并包含了它。不过,选项也是一个很好的替代方案。 - nonVirtualThunk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接