模式匹配后无法将参数化类型与具体类型匹配

时间:2019-02-04 13:42:00

标签: scala pattern-matching type-parameter algebraic-data-types

使用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

为什么要为case对象而不是case类工作?如何使它适用于案例类?

编辑

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

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是最终的。

3 个答案:

答案 0 :(得分:3)

该解决方案在@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的替代设计。这将Content超类替换为Containable类型类。现在,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)
}

答案 1 :(得分:1)

出现错误的原因是由于该方法的返回类型不明确。将返回类型从List [C]替换为List [Content]即可解决问题。

Item

答案 2 :(得分:1)

  

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

错了!

B实例中的

Singleton typesB的子类型:

val b = B(0)
val container: Container[b.type] = Container[b.type](b)

由于ContainerB不扩展Container[b.type],因此最后一行不能返回它。而且它无法更改,因此可以更改;

case class ContainerB(content: B) extends Container[content.type]

在Scala中不合法。

Null也是B的子类型,您可以创建一个类似的示例。诸如B { type T = Int }之类的优化类型也是如此。

其他子类型可能不相关,因为它们没有实例:Nothing,复合类型如B with Iterable[Int] ...