2013-03-15 95 views
9

我有两个类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 

回答

1

当然更好的解决办法是实际利用分型?

trait Object { 
    val uid: Option[String] 
    def withNewUID(newUid: String): Object 
} 
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每个case类,我认为这是不问什么),但这个工程。 :) 我认为用UidBuilder做这件事可能不是一个简单的想法,但它仍然是一个有趣的方法。

为了确保你不会忘记的情况下 - 我把它所有需要的情况下,类在同一编译单元无论如何 - 让你的Object一个sealed abstract class并添加另一个铸

this.asInstanceOf[Object] 

如果您然后为您的案例分类留下一个案例,您将收到警告。

8

我会坚持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()方法的返回类型。