2013-03-15 25 views
9

Tôi có hai lớp PixelObject, ImageRefObject và một số chi tiết khác, nhưng đây chỉ là hai lớp này để đơn giản hóa mọi thứ. Tất cả đều là các lớp con của trait Object có chứa một uid. Tôi cần phương pháp phổ quát sẽ sao chép một cá thể lớp vỏ với một số mới uid. Lý do tôi cần nó bởi vì nhiệm vụ của tôi là tạo ra một lớp ObjectRepository mà sẽ tiết kiệm dụ của bất kỳ lớp con nào của Object và trả về nó với uid mới. nỗ lực của tôi:Trường hợp bản sao Scala với loại chung

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) 

nhưng tôi nhận được lỗi sau:

➜ ~ 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 

Trả lời

1

Chắc chắn một giải pháp tốt hơn sẽ được thực sự sử dụng các subtyping?

trait Object { 
    val uid: Option[String] 
    def withNewUID(newUid: String): Object 
} 
0

Truyền tới A có thể là do định nghĩa đệ quy về các trường hợp của bạn.

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] 
    } 
    } 
} 

Có thể có một giải pháp thanh lịch hơn (trừ - thực hiện tốt withUid cho mỗi lớp trường hợp, mà tôi nghĩ là không gì bạn yêu cầu), nhưng hoạt động này. :) Tôi nghĩ rằng nó có thể không phải là một ý tưởng đơn giản làm điều này với UidBuilder, nhưng đó là một cách tiếp cận thú vị dù sao.

Để đảm bảo bạn đừng quên một trường hợp - và tôi mang nó tất cả các lớp trường hợp cần thiết là trong đơn vị biên soạn cùng anyway - làm cho bạn Object một sealed abstract class và thêm một dàn diễn viên

this.asInstanceOf[Object] 

Nếu bạn để lại một trường hợp cho một trong các trường hợp của bạn sau đó, bạn sẽ nhận được một cảnh báo.

8

Tôi sẽ tuân theo giải pháp được đề xuất bởi Seam. Tôi đã làm như vậy một vài tháng trước. Ví dụ:

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)) 
} 

Vì vậy, thay vì xác định UuiBuilder phù hợp với mọi triển khai đặc điểm của bạn, bạn xác định phương pháp trong bản thân việc triển khai. Bạn có thể không muốn sửa đổi UuiBuilder mỗi khi bạn thêm một triển khai mới.

Ngoài ra, tôi cũng khuyên bạn nên sử dụng tự nhập để thực thi kiểu trả về của phương thức withId().

Các vấn đề liên quan