Из-за стирания типа вы не можете сопоставлять шаблоны на типовых типах.List
, Map
, Option
являются универсальными контейнерами, и во время выполнения компилятор удалит типы этих универсальных контейнеров.например, List[String]
, String
будут удалены, а тип будет List[_]
.
case Some(map: List[Map[String, Any]]) => println(map)
В приведенном выше случае, если результат равен val result: Option[Any] = Some(List(12))
, то есть 12
, типом которого является Int
, а не Map[String, Any]
, компилятор все равно будет сопоставлять result
с приведенным выше случаем, дажеон ожидает Map[String, Any]]
как тип List
.
Итак, что происходит?
Все из-за стирания типа .Компилятор сотрет все типы и не будет иметь никакой информации о типах во время выполнения, если вы не используете отражение.Это означает: case Some(map: List[Map[String, Any]]) => println(map)
по существу case Some(map: List[_]) => println(map)
и, следовательно, совпадение будет успешным для любого параметра типа List
, например List[Map[String, Any]]
, List[Map[String, Int]]
, List[String]
, List[Int]
и т. Д.
Поэтому, Если вам нужно сопоставить такой универсальный контейнер, вы должны явно разрешить каждый контейнер и его вложенные подтипы.
def resolve(result: Any): Unit = result match {
case Some(res) if res.isInstanceOf[List[_]] && res.asInstanceOf[List[_]].isEmpty => println("Empty List.") //Some(List())
case Some(res) if res.isInstanceOf[List[_]] && !res.asInstanceOf[List[_]].exists(p => p.isInstanceOf[Map[_, _]] && p.asInstanceOf[Map[_, _]].nonEmpty) => println("List is not empty but each item of List is empty Map.") //Some(List(Map(), Map()))
case Some(res) if res.isInstanceOf[List[_]] && res.asInstanceOf[List[_]].filter(p => p.isInstanceOf[Map[_, _]] && p.asInstanceOf[Map[_, _]].nonEmpty).map(_.asInstanceOf[Map[_,_]]).exists(p => {p.head match {case e if e._1.isInstanceOf[String] && e._2.isInstanceOf[Any] => true; case _ => false}}) => println("Correct data.") // Some(List(Map("key1"-> 1), Map("key2" -> 2)))
case None => println("none")
case other => println("other")
}
val a: Option[Any] = Some(List())
val b: Option[Any] = Some(List(Map(), Map()))
val c: Option[Any] = Some(List(Map("key1"-> 1), Map("key2" -> 2)))
val d: Option[Any] = None
val e: Option[Any] = Some("apple")
resolve(a) // match first case
resolve(b) // match second case
resolve(c) // match third case
resolve(d) // match fourth case
resolve(e) // match fifth case