簡體   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