带有集合的问题在Scala中键入模式匹配

时间:2019-03-15 16:20:13

标签: scala generics pattern-matching scala-collections

我尝试在Scala中进行集合匹配,而不使用scala.reflect.ClassTag

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]]
}

因为:

  

警告:scala.collection.immutable.Vector [Foo](Vector [Foo]的基础)类型中的非变量类型实参Foo未被选中,因为已通过擦除将其消除了

这:

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]]”类转换来提取结果

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

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

2 个答案:

答案 0 :(得分:4)

您在这里所做的基本上是种令人不快的事情,因此我不确定是否可以以优美的方式做到这一点是一件好事,但就Shapeless而言,这是值得的TypeCase更好一点:

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))

…如果您尝试使用ClassCastException,则当然会抛出res3

这确实不是一件好事-在运行时检查类型会破坏参数性,使您的代码不那么健壮,等等。类型擦除是一件好事,而JVM上类型擦除的唯一问题是还不完整。

答案 1 :(得分:-2)

如果您需要使用隐式转换的简单操作。然后试试这个!

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))