具有泛型类型的Scala副本案例类

And*_*sov 11 generics scala case-class

我有两个类PixelObject,ImageRefObject还有一些,但这里只是这两个类来简化事情.它们都是trait Object包含uid的子类.我需要通用方法,它将使用给定的new复制case类实例uid.我需要它的原因是因为我的任务是创建一个ObjectRepository类,它将保存任何子类的实例Object并使用new返回它uid.我的尝试:

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)
Run Code Online (Sandbox Code Playgroud)

但我收到以下错误:

?  ~  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
Run Code Online (Sandbox Code Playgroud)

小智 10

我会坚持Seam提出的解决方案.几个月前我也做过同样的事情.例如:

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))
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以在实现本身中定义方法,而不是定义与特征的所有实现匹配的UuiBuilder.您可能不希望每次添加新实现时都修改UuiBuilder.

此外,我还建议您使用自键型来强制执行withId()方法的返回类型.


Sea*_*ons 1

当然更好的解决方案是实际利用子类型吗?

trait Object {
  val uid: Option[String]
  def withNewUID(newUid: String): Object
}
Run Code Online (Sandbox Code Playgroud)