[英]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.