【问题标题】:Scala: copy case class from common supertypeScala:从通用超类型复制案例类
【发布时间】:2016-04-13 01:25:23
【问题描述】:

假设我有一个基本的实体特征

trait Entity {
  final val id: Long = IdGenerator.next()
  def position: (Double, Double)
}

然后可以通过一些额外的(仍然是抽象的)功能进行扩展

sealed trait Humanoid { self: Entity =>
  def health: Double
  def name: String
}

最后,有一些具体的案例类与功能混合在一起。

case class Human(
  position: (Double, Double),
  health: Double,
  name: String
)
  extends Entity with Humanoid {

}

有了这个,假设我需要定义 Event trait,它封装了从一个实体到另一个实体的一些动作

sealed trait Event[A, B] {
  final val timestamp: Long = System.currentTimeMillis

  def from: A
  def to: B

  def event: B => B
}

现在,一些通用事件有一个案例类,它只适用于人形实体。

case class TakeDamage[A <: Entity, B <: Humanoid](damage: Int, from: A, to: B)
  extends Event[A,B] {
    val event = (ent: B) => {
      //a copy of ent with some parameters changed, e.g. health
    }
}

这应该是可能的,因为 Humanoid 超类型的所有实体都将具有所需的字段(健康)。

在没有太多样板代码的情况下,在 scala 中是否有任何类型安全且不可变的方法?还是我的抽象完全错误?

【问题讨论】:

    标签: scala generics types polymorphism


    【解决方案1】:

    我能想到的唯一“不难看”(即不涉及反射和向下转换等臭技巧)的方法是将def copy(health: Double, name: String): Humanoid 添加到Humanoid 特征中,并在子类中实现它。

    或者等价的,类似这样的:

     object Humanoid {
        def copy [T <: Humanoid](from: T, health:Double, name: String): T = from match {
           case x: Human => x.copy (health=health, name=name)
        }
      }
    

    第二种方法没有太多好处,而且不太安全(没有办法强制Humanoid的每个实现都可以与copy一起工作,你必须依赖约定和程序员的记忆,这几乎永远不会好),所以我会坚持使用旧的、好的方法覆盖,只要您可以访问类实现。

    【讨论】:

    • 看起来 shapeless 有一个关于我想要完成的示例。您对这种方法有何看法? github link
    • 我不知道,通读那段代码让我头脑发热:) 我以为你在寻找一个简单的解决方案......如果是这样的话,那就是当然不是:D。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多