[英]Overcoming type erasure in Scala when pattern matching on Objects which may be different Sets or any type of Object
當匹配的對象可以是任何對象時,是否可以通過任何方式進行對象匹配,將對象設置為Set [Foo]或Set [Bar]。
給定以下代碼,由於類型擦除,嘗試在Set [Bar]上進行模式匹配將導致Set [Foo]匹配。
import play.api.libs.json._
import scala.collection.immutable.HashMap
case class Foo(valOne: Int, valTwo: Double)
object Foo {
implicit val writesFoo = Json.writes[Foo]
}
case class Bar(valOne: String)
object Bar {
implicit val writesBar = Json.writes[Bar]
}
case class TestRequest(params: Map[String, Object])
object TestRequest {
import play.api.libs.json.Json.JsValueWrapper
implicit val writeAnyMapFormat = new Writes[Map[String, Object]] {
def writes(map: Map[String, Object]): JsValue = {
Json.obj(map.map {
case (s, a) => {
val ret: (String, JsValueWrapper) = a match {
case _: String => s -> JsString(a.asInstanceOf[String])
case _: java.util.Date => s -> JsString(a.asInstanceOf[String])
case _: Integer => s -> JsString(a.toString)
case _: java.lang.Double => s -> JsString(a.toString)
case None => s -> JsNull
case foo: Set[Foo] => s -> Json.toJson(a.asInstanceOf[Set[Foo]])
case bar: Set[Bar] => s -> Json.toJson(a.asInstanceOf[Set[Bar]])
case str: Set[String] => s -> Json.toJson(a.asInstanceOf[Set[String]])
}
ret
}}.toSeq: _*)
}
}
implicit val writesTestRequest = Json.writes[TestRequest]
}
object MakeTestRequest extends App {
val params = HashMap[String, Object]("name" -> "NAME", "fooSet" -> Set(Foo(1, 2.0)), "barSet" -> Set(Bar("val1")))
val testRequest = new TestRequest(params)
println(Json.toJson(testRequest))
}
嘗試序列化TestRequest將導致:
Exception in thread "main" java.lang.ClassCastException: Bar cannot be cast to Foo
將Set的模式匹配委托給另一個方法,以嘗試獲取TypeTag,
case _ => s -> matchSet(a)
毫不奇怪,結果就是Object的類型。
def matchSet[A: TypeTag](set: A): JsValue = typeOf[A] match {
case fooSet: Set[Foo] if typeOf[A] =:= typeOf[Foo] => Json.toJson(set.asInstanceOf[Set[Foo]])
case barSet: Set[Bar] if typeOf[A] =:= typeOf[Bar] => Json.toJson(set.asInstanceOf[Set[Bar]])
}
運行時錯誤為:
Exception in thread "main" scala.MatchError: java.lang.Object (of class scala.reflect.internal.Types$ClassNoArgsTypeRef)
一種解決方法是檢查Set中第一個元素的實例,但這似乎效率低下且難看。 也可以在鍵上匹配,例如fooSet或barSet,但是如果鍵是相同的名稱(例如都稱為set),則此鍵將不起作用。
在2.11 s中,有什么方法可以獲取創建集合的類型/類?
您可以使用Shapeless typeable 。 請注意,這仍然不是100%安全的(例如,在運行時無法區分不同類型的空列表,因為該信息實際上並不存在); 在幕后,它的工作就像使用反射檢查元素的類型,只是在頂部有一個更好的界面。
通常,最好顯式地攜帶類型信息,例如通過使用case class
(或無HMap
)而不是未類型化的Map
。 不好的情況,但總比沒有好,對可能使用的Set
的不同類型使用包裝器case class
es,以便在運行時每種類型都是不同的類型。
(模式匹配的一半是避免使用asInstanceOf
;應該使用例如foo
而不是a.asInstanceOf[Set[Foo]]
)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.