我正在尝试使用Slick将特征/类结构建模为通用数据访问层,但是为了限制基本部分,我将发布一个基本代码,该问题具有问题的基本概念。
假设我有一个基本特征,用于定义实体的公共属性。
trait Base {
def id: Option[Long]
def version: Long
}
现在我将根据该特征创建实体。
case class User(id: Option[Long], version: Long, name: String, age: Int) extends Base
一个将Base类型设为泛型的函数。那时我可以创建,更新数据库上的对象而不会出现问题。我的问题是当我想用从数据库返回的生成的id返回原始对象时。
def insert[M <: Base](m: M): M = {
//insert on database and get the new ID
val newId = Some(10)
// Now I want to return the object with the newId
// As Base is a trait I don't have copy
m
}
val user = User(None, 1, "Test", 34)
insert(user)
为了说明我想获得一个id = Some(10)的新用户作为插入函数的结果。
我想过使用copy,但如果我用Base case类而不是Trait声明函数,它会起作用,但最初不是我想要的。 我尝试使用Lens,就像scalaz Lens一样。但我也需要复制。
我错过了什么?还有另一种方法可以不使用Reflection吗?
由于
答案 0 :(得分:3)
您可以使用F绑定多态在要求返回预期类型所需的特征上使用withId
方法:
trait Base[Self <: Base[Self]] { self: Self =>
def id: Option[Long]
def version: Long
def withId(id: Long): Self
}
然后,您可以通过调用其原生withId
方法在任何案例类上实现copy
:
case class User(id: Option[Long], version: Long, name: String, age: Int) extends Base[User] {
def withId(id: Long) = this.copy(id = Some(id))
}
然后,您可以将insert
方法定义为:
def insert[M <: Base[M]](m: M): M = {
m.withId(10)
}
其余的应该按预期工作。
真正的问题是copy
是一种特殊的编译器生成方法,你不能要求它存在于特征上。这种F绑定多态性的使用允许您使用有限的样板解决此限制。
另一种方法是添加另一个特性,比如HasWithId
保证withId
方法,并在需要的地方扩展/要求:
trait Base {
def id: Option[Long]
def version: Long
}
trait HasWithId[M] {
def withId(id: Long): M
}
case class User(id: Option[Long], version: Long, name: String, age: Int) extends Base with HasWithId[User] {
def withId(id: Long): User = this.copy(id = Some(id))
}
def insert[M <: Base with HasWithId[M]](m: M) = {
m.withId(10)
}