2017-06-07 103 views
2

我有一個類的對象,其要麼有一個ID類型或其他:鏈接類型在斯卡拉

sealed trait ItemId 
case class NumericId(id: Int) extends ItemId 
case class StringId(id: String) extends ItemId 

sealed trait Item { 
    def id: ItemId 
} 

case class ItemWithNumericId(id: NumericId) extends Item 
case class ItemWithStringId(id: StringId) extends Item 

我想創建某種服務來檢索項的接口:

trait ItemService[IdType <: ItemId, ItemType <: Item] { 
    def get(id: IdType): ItemType 
} 

如何鏈接ItemId類型與Item類型把一個約束上ItemService秒到不容許這樣的:

class SillyItemService extends ItemService[NumericId, ItemWithStringId] { 
    def get(id: NumericId): ItemWithStringId = ??? 
} 

我已經意識到我可以添加泛型類型的Item類:

sealed trait ItemId 
case class NumericId(id: Int) extends ItemId 
case class StringId(id: String) extends ItemId 

sealed trait Item[Id <: ItemId] { 
    def id: Id 
} 

case class ItemWithNumericId(id: NumericId) extends Item[NumericId] 
case class ItemWithStringId(id: StringId) extends Item[StringId] 

trait ItemService[IdType <: ItemId, ItemType <: Item[IdType]] { 
    def get(id: IdType): ItemType 
} 

這是確定的,但它的超級冗長。理想情況下,該服務只會有一個通用類型。

非常感謝任何回答/輸入。

回答

2

也許這樣的事情?

trait Item { 
    type IdType 
    def id: IdType 
} 

trait ItemService[I <: Item] { 
    def get(id: I#IdType): Item 
} 
+0

這是非常好的。 ItemService的實現具有我想要的約束,並且可以用最少的樣板代碼來聲明它們。謝謝! –

2

路徑依賴的類型是由對方的回答涉及一個選項,但靈活性的目的,我會親自去與在這種情況下implicits /上下文邊界。

trait Proof[IdType <: ItemId, ItemType <: Item[IdType]] 

trait ItemService[IdType <: ItemId, ItemType <: Item[IdType]] { 
    def get(id: IdType)(implicit ev: Proof[IdType, ItemType]) 
} 

您也可以trait一個abstract class扯起隱含的證據,還是從許多可用的任何其它方招讓你避開需要包括對服務的每一個方法的證據的聲明。

然後,我會爲Proof製作一個伴侶對象,並列出您的域中可行的相關關係。

object Proof { 
    implicit numericProof: Proof[NumericId, ItemWithNumericId] = new Proof[NumericId, ItemWithNumericId] {} 
    ... 
} 

在這一點上,你真的不關心你的服務看起來像什麼,但˚F界多態性可能更高版本允許你超細粒度控制,你可以專注於具體實現implicits以及創建曖昧證據在那裏你想給編譯時錯誤的東西不意味着混合在一起。

3

這將是我的做法:

sealed trait ItemId 
case class NumericId(id: Int) extends ItemId 
case class StringId(id: String) extends ItemId 

trait Item[A <: ItemId] { 
    def id: A 
} 

trait ItemService[A <: ItemId, B <: Item[A]] { 
    def get(id: A): B 
} 

這是實話沒有太大的你做了什麼不同,我只是覺得沒有太多必要使Item特質密封,介紹了兩種實現方式就在這裏。

如果您有沒有用縮小特定ItemServiceget方法的返回類型,你甚至可以離開關B類型參數進行一步算一步簡單:

trait ItemService[A <: ItemId] { 
    def get(id: A): Item[A] 
}