代码之家  ›  专栏  ›  技术社区  ›  HoTicE

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

  •  0
  • HoTicE  · 技术社区  · 6 年前

    我尝试在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+向量或bar+向量。

    这里我们有一些问题:

    1. 我们可以做向量(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
    }
    
    0 回复  |  直到 6 年前
        1
  •  4
  •   Travis Brown    6 年前

    你在这里所做的基本上是一种不愉快的事情,所以我不确定以一种美丽的方式来做是一件好事,但为了它的价值, 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上进行类型擦除的唯一问题是它不够完整。

        2
  •  -2
  •   Rex    6 年前

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

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