Scala copy case class with generic type

case-class, generics, scala

Solution

I would stick with the solution proposed by Sean. I have done the same a couple of months ago. For example:

trait Entity[E <: Entity[E]] {
  // self-typing to E to force withId to return this type
  self: E => def id: Option[Long]
  def withId(id: Long): E
}
case class Foo extends Entity[Foo] {
  def withId(id:Long) = this.copy(id = Some(id))
}

So, instead of defining an UuiBuilder with a match for all implementations of your trait, you define the method in your implementation itself. You probably don't want to modify UuiBuilder every time you add a new implementation.

In addition, I would also recommend you to use a self typing to enforce the return type of your withId() method.

Problem

I have two classes `PixelObject`, `ImageRefObject` and some more, but here are just these two classes to simplify things. They all are subclasses of a `trait Object` that contains an uid. I need universal method which will copy a case class instance with a given new `uid`. The reason I need it because my task is to create a class ObjectRepository which will save instance of any subclass of `Object` and return it with new `uid`. My attempt: ``` trait Object { val uid: Option[String] } trait UidBuilder[A <: Object] { def withUid(uid: String): A = { this match { case x: PixelObject => x.copy(uid = Some(uid)) case x: ImageRefObject => x.copy(uid = Some(uid)) } } } case class PixelObject(uid: Option[String], targetUrl: String) extends Object with UidBuilder[PixelObject] case class ImageRefObject(uid: Option[String], targetUrl: String, imageUrl: String) extends Object with UidBuilder[ImageRefObject] val pix = PixelObject(Some("oldUid"), "http://example.com") val newPix = pix.withUid("newUid") println(newPix.toString) ``` but I am getting the following error: ``` ➜ ~ scala /tmp/1.scala /tmp/1.scala:9: error: type mismatch; found : this.PixelObject required: A case x: PixelObject => x.copy(uid = Some(uid)) ^ /tmp/1.scala:10: error: type mismatch; found : this.ImageRefObject required: A case x: ImageRefObject => x.copy(uid = Some(uid)) ^ two errors found ```

Original source