Scala 模式匹配中的集合类型问题

Troubles with collections type in pattern matching in Scala

我尝试在不使用 scala.reflect.ClassTag

的情况下在 Scala 中进行集合匹配
case class Foo(name: String)
case class Bar(id: Int)
case class Items(items: Vector[AnyRef])

val foo = Vector(Foo("a"), Foo("b"), Foo("c"))
val bar = Vector(Bar(1), Bar(2), Bar(3))

val fc = Items(foo)
val bc = Items(bar)

我们不能这样做:

fc match {
  case Items(x) if x.isInstanceOf[Vector[Foo]]
}

因为:

Warning: non-variable type argument Foo in type scala.collection.immutable.Vector[Foo] (the underlying of Vector[Foo]) is unchecked since it is eliminated by erasure

还有这个:

fc match {
  case Items(x: Vector[Foo]) =>
}

但我们可以这样做:

fc match {
  case Items(x@(_: Foo) +: _) => ...
  case Items(x@(_: Bar) +: _) => ...
}

bc match {
  case Items(x@(_: Foo) +: _) => ...
  case Items(x@(_: Bar) +: _) => ...
}

如您所见,我们正在检查 - 是集合 Foo + vector 还是 Bar + vector。

这里我们遇到了一些问题:

  1. 我们可以做 Vector(Foo("1"), Bar(2)),这将与 Foo 匹配。
  2. 我们仍然需要 "val result = x.asInstanceOf[Vector[Bar]]" class 转换结果提取

有没有一些更漂亮的方法? 像这样:

fc match {
  case Items(x: Vector[Foo]) => // result is type of Vector[Foo] already
}

你在这里做的事情从根本上来说有点令人不快,所以我不确定以漂亮的方式做这件事是不是一件好事,但就其价值而言,ShapelessTypeCase 更好一点:

case class Foo(name: String)
case class Bar(id: Int)
case class Items(items: Vector[AnyRef])

val foo = Vector(Foo("a"), Foo("b"), Foo("c"))
val bar = Vector(Bar(1), Bar(2), Bar(3))

val fc = Items(foo)
val bc = Items(bar)

val FooVector = shapeless.TypeCase[Vector[Foo]]
val BarVector = shapeless.TypeCase[Vector[Bar]]

然后:

scala> fc match {
     |   case Items(FooVector(items)) => items
     |   case _ => Vector.empty
     | }
res0: Vector[Foo] = Vector(Foo(a), Foo(b), Foo(c))

scala> bc match {
     |   case Items(FooVector(items)) => items
     |   case _ => Vector.empty
     | }
res1: Vector[Foo] = Vector()

请注意,虽然 ClassTag 个实例也可以以这种方式使用,但它们不会执行您想要的操作:

scala> val FooVector = implicitly[scala.reflect.ClassTag[Vector[Foo]]]
FooVector: scala.reflect.ClassTag[Vector[Foo]] = scala.collection.immutable.Vector

scala> fc match {
     |   case Items(FooVector(items)) => items
     |   case _ => Vector.empty
     | }
res2: Vector[Foo] = Vector(Foo(a), Foo(b), Foo(c))

scala> bc match {
     |   case Items(FooVector(items)) => items
     |   case _ => Vector.empty
     | }
res3: Vector[Foo] = Vector(Bar(1), Bar(2), Bar(3))

…如果您尝试使用 res3.

,这当然会抛出 ClassCastExceptions

不过,这确实不是一件好事——在运行时检查类型会破坏参数化,降低代码的健壮性等。类型擦除是一件好事,也是 JVM 上类型擦除的唯一问题就是还不够完整

如果您想要使用隐式转换的简单方法。然后试试这个!

implicit def VectorConversionI(items: Items): Vector[AnyRef] = items match { case x@Items(v) => v }

Example:

val fcVertor: Vector[AnyRef] = fc // Vector(Foo(a), Foo(b), Foo(c))

val bcVertor: Vector[AnyRef] = bc // Vector(Bar(1), Bar(2), Bar(3))