我有两个类PixelObject
,ImageRefObject
等等,但这里只是为了简化这两个类。它们都是包含uid的trait Object
的子类。
我需要通用方法,它将使用给定的新uid
复制案例类实例。我需要它的原因是因为我的任务是创建一个ObjectRepository类,它将保存Object
的任何子类的实例并使用新的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)
但是我收到以下错误:
➜ ~ 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
答案 0 :(得分:9)
我会坚持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))
}
因此,您可以在实现本身中定义方法,而不是定义与特征的所有实现匹配的UuiBuilder。您可能不希望每次添加新实现时都修改UuiBuilder。
此外,我还建议您使用自我输入来强制执行withId()方法的返回类型。
答案 1 :(得分:1)
当然,更好的解决方案是实际利用子类型吗?
trait Object {
val uid: Option[String]
def withNewUID(newUid: String): Object
}
答案 2 :(得分:0)
转换为A可以解决问题 - 可能是由于您的案例类的递归定义。
trait UidBuilder[A <: Object] {
def withUid(uid: String): A = {
this match {
case x: PixelObject => x.copy(uid = Some(uid)).asInstanceOf[A]
case x: ImageRefObject => x.copy(uid = Some(uid)).asInstanceOf[A]
}
}
}
也许有一个更优雅的解决方案(除了 - 为每个案例类实现withUid,我认为不是你所要求的),但这是有效的。 :) 我认为使用UidBuilder这可能不是一个直截了当的想法,但它仍然是一个有趣的方法。
为了确保你不会忘记一个案例 - 我认为所有必要的案例类都在同一个编译单元中 - 让你的Object
成为sealed abstract class
并添加另一个演员
this.asInstanceOf[Object]
如果您遗漏了一个案例类别的案例,那么您将收到警告。