首页 > 解决方案 > 从具有参数化类型参数的另一个方法调用具有参数化类型参数的方法时保留特定的结果类型参数

问题描述

假设我正在尝试使用继承对实体、元数据和存储库进行建模

trait EntityMetadata {
  def maybeVersion: Option[Int]
}

// For creation
case object NoMetadata extends EntityMetadata {
  def maybeVersion: Option[Int] = None
}

// For other cases
final case class VersionedMetadata(version: Int) extends EntityMetadata {

  def maybeVersion: Option[Int] = Some(version)
}

trait Entity[Meta <: EntityMetadata] {
  type Id
  def id: Id
  def meta: Meta // Meta is paremeterised
}

如果我然后尝试创建一个特征来保存通用后备存储的一些方法,在我看来,即使类型是已知的......我实际上不能正确使用它们?

trait BackingStore {

  // Method for retrieving an entity by id
  // `Meta` doesn't really matter here, but we can't
  // wild-card it. We return the Entity with VersionedMetadata
  // since it's been stored if we can find it
  def getFromStore[Meta <: EntityMetadata, E[_] <: Entity[_]](
    id: E[Meta]#Id
  ): Option[E[VersionedMetadata]]

  // Just for demo purposes, try to retrieve something by id
  // and return its metadata version
  def getVersion[Meta <: EntityMetadata, E[_] <: Entity[_]](
    id: E[Meta]#Id
  ): Option[Long] = getFromStore(id).map { retrieved =>
    // So far so good, we know it's E[VersionedMetadata]
    val typeTest: E[VersionedMetadata] = retrieved

    //
    // value version is not a member of _$2
    // typeTest.meta.version // complains about version
    //
    retrieved.meta.version // complains about version

  }

}

我正在努力解决:

  1. 为什么编译器认为retrieved.meta没有.version,或者事实上,任何超出Any/Object的东西。
  2. 我能做些什么来完成这项工作

标签: scalagenericsinheritancemethodsexistential-type

解决方案


尝试修复签名

def getFromStore[Meta <: EntityMetadata, E[M <: EntityMetadata] <: Entity[M]](
  id: E[Meta]#Id
): Option[E[VersionedMetadata]]

def getVersion[Meta <: EntityMetadata, E[M <: EntityMetadata] <: Entity[M]](
  id: E[Meta]#Id
): Option[Long]

E[_]Entity[_]inE[_] <: Entity[_]不同:E[_]是一个类型构造函数E[M](即每个类型都可以有一个类型M),Entity[_]Entity[Meta] forSome { type Meta }就是存在类型。存在类型没有.versionretrieved.meta是类型Any)。


修复代码的另一种方法是

def getFromStore[Meta <: EntityMetadata, E[_] <: Entity[_]](
  id: E[Meta]#Id
): Option[E[VersionedMetadata]]

def getVersion[Meta <: EntityMetadata, E[_] <: Entity[_ <: EntityMetadata]](
  id: E[Meta]#Id
): Option[Int] = getFromStore(id).flatMap { retrieved =>
  val typeTest: E[VersionedMetadata] = retrieved

  retrieved.meta.maybeVersion
}

<: EntityMetadata我保留了类型构造函数和存在类型,但为存在类型的参数添加了上限Entity[_ <: ...],这是类型参数的上限E[_] <: ...。现在retrieved.meta的类型是 的子类型,EntityMetadata所以它有.maybeVersion而不是.version(并且map应该替换为flatMap)。也Long应该换成Int.

或者你可以设置上限_ <: VersionedMetadata而不是我的<: EntityMetadata. 然后你可以保留.version和。.mapLong


推荐阅读