scala - 模式匹配后无法将参数化类型与具体类型匹配
问题描述
使用 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 是最终的。
解决方案
解决方案在@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)
}
推荐阅读
- r - 从列中的行中删除 NA 和空白行
- mysql - 尝试在 mysqli 上使用两个选择查询进行 LEFT JOIN
- python - 如何使 QPushButton 成为加载按钮?
- node.js - Express JS 处理程序中的 res.send() 是否会自动调用 next()?
- python - AttributeError: 'RDD' 对象没有属性 'show'
- python - Zipline 基本数据
- git - 由于文件名太长的问题,无法签出 Mule 代码
- ethereum - Ropsten(以太坊测试网络)交易无法解决(仍待处理),使用 Web3js
- tfs - TFS2018:暂停和恢复后执行的并行构建
- java - Webstart 上的多个 JAR 应用程序