首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >模式匹配中的Scala并类型

模式匹配中的Scala并类型
EN

Stack Overflow用户
提问于 2017-03-25 12:39:46
回答 2查看 1.9K关注 0票数 1

我有一个这样的trait

代码语言:javascript
复制
trait Identifiable {

  def id: Option[Long]

}

还有一些其他的case classes扩展了Identifiable的特性。例如:

代码语言:javascript
复制
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

最简单的方法似乎是:

代码语言:javascript
复制
val xs: Seq[Identifiable] = ...
xs.map {
  case x: EntityA => x.copy(id = Some(nextId))
  case x: EntityB => x.copy(id = Some(nextId))
}

好的!但有个问题。子类越多,要编写的代码就越多.

我试着从工会类型那里得到帮助

代码语言:javascript
复制
xs.map {
  case x: EntityA with EntityB => x.copy(id = Some(nextId))
}

代码语言:javascript
复制
xs.map {
  case x @ (_: EntityA | _: EntityB) => x.copy(id = Some(nextId))
}

但我有个错误说:Cannot resolve symbol copy

任何帮助都将不胜感激。谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-03-25 15:40:40

基本上,我们在这里要做的是对实际类型的抽象。这方面的问题是,copy仅在案例类方面实现OOTB,而Identifiable是一个特性,因此编译时可能有或可能没有可用的copy方法,因此编译器对您大喊大叫。

受到this answer的强烈启发,我修改了所提供的使用无形状透镜的示例:

代码语言:javascript
复制
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]

代码语言:javascript
复制
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)

产量:

代码语言:javascript
复制
EntityA(Some(-2485820339267038236),,Thu Feb 01 00:00:00 IST 3917)
EntityB(Some(2288888070116166731),100,1)

对于@Kolmar在上面提供的链接中组装这个答案的各个部分,有一个很好的解释,所以首先阅读另一个答案的透镜工作原理的细节(这非常相似),然后回到这里,作为一个最小工作示例的参考。

还请参阅@Jasper answer here,以获得更多实现该功能的方法。

票数 2
EN

Stack Overflow用户

发布于 2017-03-25 15:59:25

在这里,联合类型不是正确的路径。考虑:

代码语言:javascript
复制
xs.map {
  case x @ (_: EntityA | _: EntityB) => x.copy(id = Some(nextId))
}

当您说EntityA \ EntityB时,Scala将尝试找到将这两种类型连接在一起的超级类型。在这种情况下,它是可识别的,它没有复制方法,因此编译器无法解析它。

下一步:

代码语言:javascript
复制
xs.map {
  case x: EntityA with EntityB => x.copy(id = Some(nextId))
}

当你用EntityA表示EntityB时,你说的是"x同时是一个EntityA和EntityB的类型“。不存在这样的类型,当然也不存在有复制方法的类型。

不幸的是,我认为您不能像在普通Scala中那样对copy方法进行一般性抽象。我认为您的最佳选择是在您的特性中添加一个复制方法,并在您的每个子类中实现类似的方法,不幸的是,这意味着一些样板:

代码语言:javascript
复制
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库来完成此操作。看看这个答案,这个答案和你想要做的非常相似:

Case to case inheritence in Scala

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43016431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档