Return 来自输入参数基于特征的 scala 函数的新修改字段对象

Return a new modified field object from a scala function that the input parameter is based on a trait

我正在尝试使用 Slick 将 trait/class 结构建模为通用数据访问层,但为了仅限制基本部分,我将 post 具有总体思路的基本代码的问题。

假设我有一个定义实体公共属性的基本特征。

trait Base {
  def id: Option[Long]
  def version: Long
} 

现在我将根据该特征创建实体。

case class User(id: Option[Long], version: Long, name: String, age: Int) extends Base

还有一个函数可以使 Base 类型成为泛型。那时我可以毫无问题地创建、更新数据库中的对象。我的问题是当我想 return 具有从数据库中生成的 ID return 的原始对象时。

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) 的新用户作为插入函数的结果。

我考虑过使用副本,但如果我用基本案例 class 而不是 Trait 声明函数,它会起作用,但这不是我最初想要的。 我尝试使用 Lens,例如 scalaz Lens。但我也需要复印件。

我错过了什么?还有另一种不使用反射的方法吗?

谢谢

您可以使用 F-bound 多态性在 return 预期类型所需的特征上要求 withId 方法:

trait Base[Self <: Base[Self]] { self: Self =>
  def id: Option[Long]
  def version: Long
  def withId(id: Long): Self
} 

然后您可以通过调用其本机 copy 方法在任何情况下 class 实现 withId

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 方法,extend/require 在任何需要的地方:

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)
}