Проблемы с типом коллекций при сопоставлении с образцом в Scala - PullRequest
1 голос
/ 15 марта 2019

Я пытался выполнить сопоставление коллекций в 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]]
}

, потому что:

Предупреждение: аргумент не-переменного типа Foo в типе scala.collection.immutable.Vector [Foo] (базовый элемент Vector [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 ]

4 голосов
/ 15 марта 2019

То, что вы здесь делаете, в принципе просто неприятно, так что я не уверен, что сделать это красиво - это хорошо, но за все это стоит, Бесформенный 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 состоит в том, что он не является более полным.

0 голосов
/ 15 марта 2019

Если вы хотите что-то простое, используя неявные преобразования. тогда попробуй это!

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