首页 > 解决方案 > 模式匹配后无法将参数化类型与具体类型匹配

问题描述

使用 scala 2.12.8 这不会在没有强制转换的情况下编译:

trait Content
case object A extends Content
case class B(i: Int) extends Content

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(b) // does not compile
}
type mismatch;
 found   : b.type (with underlying type Playground.this.B)
 required: C

这是问题的 Scastie 链接:https ://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ

为什么为案例对象而不是案例类工作?我怎样才能使它适用于案例类?

编辑

第一个答案让我意识到我过度简化了我的问题,这是一个更新的版本:

sealed trait Content
case object A extends Content
final case class B(i: Int) extends Content

sealed trait Container[+C <: Content]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

object Container {
  def apply[C <: Content](content: C): Container[C] = content match {
    case A => ContainerA(A) // compiles
    case b: B => ContainerB(b) // does not compile
  }
}

Scastie 链接:https ://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ

C 不能是 B 的子类型,因为 B 是最终的。

标签: scalapattern-matchingtype-parameteralgebraic-data-types

解决方案


解决方案在@lasf 的评论中给出:

def asList[C <: Content](content: C): List[C] = content match {
  case A => List(A) // compiles
  case b: B => List(content) // compiles
}

问题是返回类型是List[C]但编译器不能保证的类型List(b)List[C]。特别是,C可能是 的子类型,B在这种情况下List(b),将List[B]与 不兼容List[C]


更新后的版本可以用 解决asInstanceOf,虽然不是很漂亮。

def apply[C <: Content](content: C): Container[C] = content match {
  case A => ContainerA(A) // compiles
  case b: B => ContainerB(b).asInstanceOf[Container[C]]
}

或者,您可以采用不同的方法并使用隐式转换:

object Container {
  implicit def contain(content: A.type): Container[A.type] = ContainerA(content)
  implicit def contain(content: B): Container[B] = ContainerB(content)
}

val ca: Container[A.type] = A
val cb: Container[B] = B(0)

甚至是多个构造函数:

object Container {
  def apply(content: A.type): Container[A.type] = ContainerA(content)
  def apply(content: B): Container[B] = ContainerB(content)
}

这是使用typeclass的替代设计。这用类型类替换了ContentContainable类。该类Container现在可以包含任何内容,只要该类有一个实例即可Containable

case object A
case class B(i: Int)

sealed trait Container[C]
case class ContainerA(content: A.type) extends Container[A.type]
case class ContainerB(content: B) extends Container[B]

trait Containable[T] {
  def apply(value: T): Container[T]
}
object Containable {
  implicit object AContainer extends Containable[A.type] {
    def apply(value: A.type) = ContainerA(value)
  }
  implicit object BContainer extends Containable[B] {
    def apply(value: B) = ContainerB(value)
  }
}

object Container {
  def apply[C](content: C)(implicit containable: Containable[C]): Container[C] =
    containable(content)
}

推荐阅读