是否可以更新实现共同特征的任何案例类的字段

时间:2014-04-21 13:08:48

标签: scala case-class shapeless

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

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

是否可以编写如下所示的实用程序函数,该函数将Model作为参数并返回带有更新字段的更新值的副本?

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

3 个答案:

答案 0 :(得分:4)

你可以在这里写的“最佳”抽象是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)

答案 1 :(得分:3)

您的代码有两个问题:

  1. copy未在特征上定义,因此您需要在特征上定义可以使用的特征。
  2. 为了让update返回T而不是Model,每个Model必须知道其实际的子类型。
  3. 你可以这样解决:

    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)
    

答案 2 :(得分:1)

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方法:)