[英]Overcoming type erasure in Scala when pattern matching on Objects which may be different Sets or any type of Object
Is there any way of pattern matching objects where the objects may be Set[Foo] or Set[Bar] when the matching object can be any Object. 当匹配的对象可以是任何对象时,是否可以通过任何方式进行对象匹配,将对象设置为Set [Foo]或Set [Bar]。
Given the below code, trying to pattern match on Set[Bar] will result in a match of Set[Foo] because of type erasure. 给定以下代码,由于类型擦除,尝试在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))
}
Trying to serialise the TestRequest will result in: 尝试序列化TestRequest将导致:
Exception in thread "main" java.lang.ClassCastException: Bar cannot be cast to Foo
Delegating the pattern matching of Sets to another method in an attempt to get the TypeTag, 将Set的模式匹配委托给另一个方法,以尝试获取TypeTag,
case _ => s -> matchSet(a)
results in the type, unsurprisingly, of Object. 毫不奇怪,结果就是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]])
}
The runtime error being: 运行时错误为:
Exception in thread "main" scala.MatchError: java.lang.Object (of class scala.reflect.internal.Types$ClassNoArgsTypeRef)
A workaround could be to check the instance of the first element in the Set but this seems inefficient and ugly. 一种解决方法是检查Set中第一个元素的实例,但这似乎效率低下且难看。 Could also match on the key eg fooSet or barSet but if the keys are the same name eg both called set, then this wouldn't work.
也可以在键上匹配,例如fooSet或barSet,但是如果键是相同的名称(例如都称为set),则此键将不起作用。
In 2.11 s there any way to get at the type/class the Set has been created with? 在2.11 s中,有什么方法可以获取创建集合的类型/类?
You could use Shapeless typeable . 您可以使用Shapeless typeable 。 Note that this is still not 100% safe (eg empty lists of different types cannot be distinguished at runtime, because that information literally doesn't exist);
请注意,这仍然不是100%安全的(例如,在运行时无法区分不同类型的空列表,因为该信息实际上并不存在); under the hood it's doing things like checking the types of the elements using reflection, just with a nicer interface on top.
在幕后,它的工作就像使用反射检查元素的类型,只是在顶部有一个更好的界面。
In general it's better to carry the type information around explicitly, eg by using aa case class
(or a shapeless HMap
) rather than an untyped Map
. 通常,最好显式地携带类型信息,例如通过使用
case class
(或无HMap
)而不是未类型化的Map
。 Less good, but still better than nothing, is using wrapper case class
es for the different types of Set
that are possible, so that each one is a different type at runtime. 不好的情况,但总比没有好,对可能使用的
Set
的不同类型使用包装器case class
es,以便在运行时每种类型都是不同的类型。
(Also half the point of the pattern match is to avoid the asInstanceOf
; you should use eg foo
rather than a.asInstanceOf[Set[Foo]]
) (模式匹配的一半是避免使用
asInstanceOf
;应该使用例如foo
而不是a.asInstanceOf[Set[Foo]]
)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.