【问题标题】:Scala copy case class with generic type具有泛型类型的 Scala 复制案例类
【发布时间】:2013-03-15 20:32:26
【问题描述】:

我有两个类PixelObjectImageRefObject 等等,但这里只是这两个类来简化事情。它们都是包含 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

【问题讨论】:

    标签: scala generics case-class


    【解决方案1】:

    我会坚持 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))
    }
    

    因此,您无需为您的 trait 的所有实现定义一个匹配的 UuiBuilder,而是在您的实现本身中定义该方法。您可能不想在每次添加新实现时都修改 UuiBuilder。

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

    【讨论】:

    • 不编译。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      转换为 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]
      

      如果您为其中一个案例类别遗漏了一个案例,您将收到警告。

      【讨论】:

        猜你喜欢
        • 2015-07-09
        • 2020-03-27
        • 1970-01-01
        • 2016-04-13
        • 1970-01-01
        • 2018-10-23
        • 1970-01-01
        • 2012-10-22
        • 2019-02-02
        相关资源
        最近更新 更多