【问题标题】:Is it possible to update fields of any case class implementing a common trait是否可以更新任何实现共同特征的案例类的字段
【发布时间】:2014-04-21 13:08:48
【问题描述】:

假设我们有一个共同的特征模型。

trait Model {
  def id: String
  def updated: Date
}

我们有 2 个案例类扩展了这个特性。

case class C1(id: String, updated: Date, foo: String) extends Model
case class C2(id: String, updated: Date, bar: Int) extends Model

是否可以编写一个像下面这样的实用函数,它将模型作为参数并返回一个包含更新字段的更新值的副本?

object Model {
    def update[T <: Model](model: T): T = {
        model.copy(updated = new Date) // This code does not compile.
    }
}

【问题讨论】:

标签: scala case-class shapeless


【解决方案1】:

您可以在此处编写的“最佳”抽象是 Lens,如下所示:

trait Lens[A, B]{
  def get: A => B
  def set: (A, B) => A
}

让你的代码看起来像:

def update[A](that: A, value: Date)(implicit tLens: Lens[A, Date]): A =
  tLens set (that, value)

【讨论】:

  • 镜头很好......但我想我们可以推断镜头给定一个无形的LabelledGeneric 用于相关案例类别。
  • @MilesSabin 是的,我打赌你是对的。如果你写出来,我会提高答案。
【解决方案2】:

您的代码有两个问题:

  1. copy 未在特征上定义,因此您需要在特征上定义一些可以使用的内容。
  2. 为了让update 返回T 而不是Model,每个Model 都必须知道它的实际子类型。

你可以这样修复它:

trait Model[T <: Model[T]] {
  def id: String
  def updated: Date
  def withDate(d: Date): T
}

case class C1(id: String, updated: Date, foo: String) extends Model[C1] { def withDate(d: Date) = copy(updated = d) }
case class C2(id: String, updated: Date, bar: Int)    extends Model[C2] { def withDate(d: Date) = copy(updated = d) }

object Model {
  def update[T <: Model[T]](model: T): T = {
    model.withDate(new Date) // This code does not compile.
  }
}

所以现在可以了:

scala> val c1 = C1("test", new Date, "foo")
c1: C1 = C1(test,Mon Apr 21 10:25:10 CDT 2014,foo)

scala> Model.update(c1)
res0: C1 = C1(test,Mon Apr 21 10:25:17 CDT 2014,foo)

【讨论】:

  • 这是我计划采取的方式。但我仍然想知道是否有办法自动执行此操作并输入安全。
  • 所以即使withDateModel 的每个子类型的同一行代码,它也需要为每个类重写,对吗?
【解决方案3】:

copy 是在您的案例类中定义的方法。不是你的基本特征Model。如果你有这个怎么办:

trait Model {
  def id: String
  def updated: Date
}

case class C1(id: String, updated: Date, foo: String) extends Model
case class C2(id: String, updated: Date, bar: Int) extends Model
class NotACaseClass(val id: String, val updated: Date) extends Model 

NotACaseClassModel 的一个非常有效的子代,您可以将它的一个实例传递给您的update 函数,但祝您找到copy 方法的好运:)

【讨论】:

  • 它回答了为什么它不起作用,但没有提供给定问题的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-19
  • 2021-11-14
  • 2019-02-09
  • 2022-10-02
  • 1970-01-01
  • 2017-06-27
  • 1970-01-01
相关资源
最近更新 更多