我有一类具有一种ID或另一种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
}
这是可以的,但它超级详细。理想情况下,该服务只有一个通用类型。
非常感谢任何答案/输入。
答案 0 :(得分: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
特征密封并在那里引入两个实现。
如果您没有使用缩小特定get
的{{1}}方法的返回类型,您甚至可以不使用ItemService
类型参数来简化操作:
B
答案 1 :(得分:2)
这样的事可能吗?
trait Item {
type IdType
def id: IdType
}
trait ItemService[I <: Item] {
def get(id: I#IdType): Item
}
答案 2 :(得分:2)
路径依赖类型是另一个答案所涵盖的一个选项,但出于灵活性的考虑,我个人会在这种情况下使用implicits / context bounds。
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] {}
...
}
此时你并不关心你的服务是什么样的,虽然有限的多态性可能会在以后允许你进行超细粒度控制,因为你可以专门针对特定的实现进行暗示,以及创建模糊的证据。想要为那些不能混合在一起的东西提供编译时错误。