繁体   English   中英

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

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

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