11

私には 2 つのクラスがありPixelObjectImageRefObjectさらにいくつかのクラスがありますが、簡単にするためにこれら 2 つのクラスだけを示します。それらはすべてtrait Object、uid を含む a のサブクラスです。ケースクラスインスタンスを指定された new でコピーするユニバーサルメソッドが必要uidです。Object私のタスクは、サブクラスのインスタンスを保存し、それを new で返すクラス ObjectRepository を作成することであるため、必要な理由です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
4

3 に答える 3

11

私は 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() メソッドの戻り値の型を強制するために自己型付けを使用することもお勧めします。

于 2013-03-15T21:39:38.437 に答える
1

確かに、より良い解決策は、実際にサブタイピングを利用することでしょうか?

trait Object {
  val uid: Option[String]
  def withNewUID(newUid: String): Object
}
于 2013-03-15T20:41:27.313 に答える
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 でこれを行うのは単純なアイデアではないかもしれませんが、それでも興味深いアプローチです。

ケースを忘れないようにするために-必要なすべてのケースクラスはとにかく同じコンパイルユニットにあると思います- Objectaを作成sealed abstract classし、別のキャストを追加します

this.asInstanceOf[Object]

ケース クラスの 1 つのケースを除外すると、警告が表示されます。

于 2013-03-15T20:50:17.640 に答える