2011-12-08 101 views
4

我對Scala相當陌生,但仍然對其複雜的類型系統感到困惑。繼承保留類型的特徵

我嘗試解決以下問題。我想設計可變雙向鏈表(以及更復雜的數據結構,如堆)的成員類。當然,我的目標是能夠在常量時間內從數據結構中刪除對象。

我設計了以下特點:

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

    ... 
} 

與從列表中添加和刪除對象了幾個方法。

的問題是,當我在我的實際類,說:

class IntInDL(val v: Int) extends DLLMember 

當解析我的名單,IntInDL.next將返回我DLLMember類型,而不是IntInDL,我要投給檢索值:這是不好的...

有沒有辦法利用Scala的類型系統來保持我的工作類型安全?

回答

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必須是類的超這個特點是應用於。當您使用IntInDL中的特徵時,現在知道nextprev變量必須是IntInDL的某個子類型,因爲您提供了該變量作爲類型參數。因此,您可以直接使用他們的成員,而無需投射。

如果您提供了一些不屬於層次結構一部分的其他任意類型,例如class IntInDL(val v: Int) extends DLLMember[String],那麼它將無法編譯。

+0

它很好地工作,但你會如何允許空值接下來和prev? (除了把它包裝在Options中) – scand1sk

+0

@ scand1sk你必須在類型參數上添加一個額外的邊界,我已經編輯了包含它的答案。儘管如此,選項是一個很好的選擇。 – nonVirtualThunk