简体   繁体   English

在对象上进行模式匹配时可以克服Scala中的类型擦除,这些对象可能是不同的Set或任何类型的Object

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM