模式匹配后无法将参数化类型与具体类型匹配
Unable to match a parameterized type with a concrete type after pattern-matching
使用 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 link 问题:https://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ
为什么针对案例对象而不是案例 class?我怎样才能让它适用于案例 class?
编辑
第一个答案让我意识到我把问题简单化了,这里是更新版本:
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
}
}
斯卡斯蒂 link: https://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ
C 不能是 B 的子类型,因为 B 是最终类型。
您收到错误的原因是方法的 return 类型不明确。将 return 类型从 List[C] 替换为 List[Content] 即可解决问题。
def asList[C <: Content](content: C): List[Content] = content match {
case A => List(A) // compiles
case b: B => List(b) // compiles
}
解决方法在@lasf的评论中给出:
def asList[C <: Content](content: C): List[C] = content match {
case A => List(A) // compiles
case b: B => List(content) // compiles
}
问题是return的类型是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
superclass 替换为 Containable
类型 class。 Container
class 现在可以包含任何内容,只要存在 class.
的 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)
}
C cannot be a subtype of B since B is final.
错了!
Singleton types 个 B
个实例是 B
:
的子类型
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]
...
使用 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 link 问题:https://scastie.scala-lang.org/JIziYOYNTwKoZpdCIPCvdQ
为什么针对案例对象而不是案例 class?我怎样才能让它适用于案例 class?
编辑
第一个答案让我意识到我把问题简单化了,这里是更新版本:
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
}
}
斯卡斯蒂 link: https://scastie.scala-lang.org/TDlJM5SYSwGl2gmQPvKEXQ
C 不能是 B 的子类型,因为 B 是最终类型。
您收到错误的原因是方法的 return 类型不明确。将 return 类型从 List[C] 替换为 List[Content] 即可解决问题。
def asList[C <: Content](content: C): List[Content] = content match {
case A => List(A) // compiles
case b: B => List(b) // compiles
}
解决方法在@lasf的评论中给出:
def asList[C <: Content](content: C): List[C] = content match {
case A => List(A) // compiles
case b: B => List(content) // compiles
}
问题是return的类型是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
superclass 替换为 Containable
类型 class。 Container
class 现在可以包含任何内容,只要存在 class.
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)
}
C cannot be a subtype of B since B is final.
错了!
Singleton types 个 B
个实例是 B
:
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]
...