我有一个这样的trait:
trait Identifiable {
def id: Option[Long]
}还有一些其他的case classes扩展了Identifiable的特性。例如:
case class EntityA(id: Option[Long], name: String, created: Date) extends Identifiable
case class EntityB(id: Option[Long], price: Long, count: Int) extends Identifiable假设我有一个Seq[Identifiable],并且希望为每个id分配新的id。
最简单的方法似乎是:
val xs: Seq[Identifiable] = ...
xs.map {
case x: EntityA => x.copy(id = Some(nextId))
case x: EntityB => x.copy(id = Some(nextId))
}好的!但有个问题。子类越多,要编写的代码就越多.
我试着从工会类型那里得到帮助
xs.map {
case x: EntityA with EntityB => x.copy(id = Some(nextId))
}或
xs.map {
case x @ (_: EntityA | _: EntityB) => x.copy(id = Some(nextId))
}但我有个错误说:Cannot resolve symbol copy
任何帮助都将不胜感激。谢谢。
发布于 2017-03-25 15:40:40
基本上,我们在这里要做的是对实际类型的抽象。这方面的问题是,copy仅在案例类方面实现OOTB,而Identifiable是一个特性,因此编译时可能有或可能没有可用的copy方法,因此编译器对您大喊大叫。
受到this answer的强烈启发,我修改了所提供的使用无形状透镜的示例:
import shapeless._
abstract class Identifiable[T](implicit l: MkFieldLens.Aux[T, Witness.`'id`.T, Option[Long]]){
self: T =>
final private val idLens = lens[T] >> 'id
def id: Option[Long]
def modifyId(): T = idLens.modify(self)(_ => Some(Random.nextLong()))
}
case class EntityA(id: Option[Long], name: String, create: Date) extends Identifiable[EntityA]
case class EntityB(id: Option[Long], price: Long, count: Int) extends Identifiable[EntityB]现在,我们可以在任何类型的扩展id上免费修改每个Identifable[T]:
val xs: Seq[Identifiable[_]] = Seq(EntityA(Some(1), "", new Date(2017, 1, 1)), EntityB(Some(2L), 100L, 1))
val res = xs.map(_.modifyId())
res.foreach(println)产量:
EntityA(Some(-2485820339267038236),,Thu Feb 01 00:00:00 IST 3917)
EntityB(Some(2288888070116166731),100,1)对于@Kolmar在上面提供的链接中组装这个答案的各个部分,有一个很好的解释,所以首先阅读另一个答案的透镜工作原理的细节(这非常相似),然后回到这里,作为一个最小工作示例的参考。
还请参阅@Jasper answer here,以获得更多实现该功能的方法。
发布于 2017-03-25 15:59:25
在这里,联合类型不是正确的路径。考虑:
xs.map {
case x @ (_: EntityA | _: EntityB) => x.copy(id = Some(nextId))
}当您说EntityA \ EntityB时,Scala将尝试找到将这两种类型连接在一起的超级类型。在这种情况下,它是可识别的,它没有复制方法,因此编译器无法解析它。
下一步:
xs.map {
case x: EntityA with EntityB => x.copy(id = Some(nextId))
}当你用EntityA表示EntityB时,你说的是"x同时是一个EntityA和EntityB的类型“。不存在这样的类型,当然也不存在有复制方法的类型。
不幸的是,我认为您不能像在普通Scala中那样对copy方法进行一般性抽象。我认为您的最佳选择是在您的特性中添加一个复制方法,并在您的每个子类中实现类似的方法,不幸的是,这意味着一些样板:
trait Identifiable {
def id: Option[Long]
def copyWithNewId(newId: Option[Long]): Identifiable
}
case class EntityA(id: Option[Long], name: String) extends Identifiable {
override def copyWithNewId(newId: Option[Long]) = this.copy(id = newId)
}
case class EntityB(id: Option[Long], count: Int) extends Identifiable {
override def copyWithNewId(newId: Option[Long]) = this.copy(id = newId)
}这或多或少与您的工作模式匹配有关,除了将复制调用移动到实体本身之外。
现在,这只适用于普通Scala。您可以使用更高级的库,例如无形库或Monocle库来完成此操作。看看这个答案,这个答案和你想要做的非常相似:
https://stackoverflow.com/questions/43016431
复制相似问题