美文网首页
Scala中泛型类型获取

Scala中泛型类型获取

作者: jupiter_2000 | 来源:发表于2018-11-08 23:36 被阅读0次

背景

笔者最近尝试使用case class作为ActiveMQ消息的定义。同时实现,在消息发送前,将case class实例对象序列化成Json字符串;在消息接收到后,可以将消息中的Json字符串反序列化为cass class对象。上述工作抽象成trait来实现,让每一个case class定义的消息都混入该trait,以实现代码复用。由于使用scala编码,所以Json的序列化和反序列化使用的json4s组件。

case class Person为序列化后发送的消息,object Person负责将Json字符串反序列化为case class Person对象。它们分别混入了序列化和反序列化的trait,TransformToJsonTransformToObj.

消息体

package io.neal.json.message

import io.neal.json.{TransformToJson, TransformToObj}

case class Person(name: String, age: Int, email: String) extends TransformToJson
object Person extends TransformToObj[Person]

序列化trait

import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{write => jWrite}

trait TransformToJson {
  def toJson: String = {
    implicit val format: AnyRef with Formats= Serialization.formats(NoTypeHints)
    jWrite(this)
  }
}

反序列化trait

import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read => jRead}

trait TransformToObj[A] {
  def toObj(json: String): A = {
    implicit val format: AnyRef with Formats= Serialization.formats(NoTypeHints)
    jRead[A](json)
  }
}

测试用例

package io.neal.json.message

import org.scalatest.FlatSpec

class PersonTest extends FlatSpec {
  private val personObj = Person("neal", 4, "neal@sina.com")
  private val personJson ="""{"name":"neal","age":4,"email":"neal@sina.com"}"""

  "Instance of Person converts to json string" should "be ok" in {
    assertResult(personJson)(personObj.toJson)
  }

  "Json String converts to instance of Person" should "be ok" in {
    assertResult(personObj)(Person.toObj(personJson))
  }
}

问题

运行测试用例出现以下编译错误

Error:(11, 13) No Manifest available for A.
    jRead[A](json)
Error:(11, 13) not enough arguments for method read: (implicit formats: org.json4s.Formats, implicit mf: scala.reflect.Manifest[A])A.
Unspecified value parameter mf.
    jRead[A](json)

如果将TransformToObj中的jRead[A](json)修改为jRead(json),编译可以通过,但第二条测试用例抛出异常

Parsed JSON values do not match with class constructor
args=
arg types=
executable=Executable(Constructor(public scala.runtime.Nothing$()))
cause=null
types comparison result=
org.json4s.package$MappingException: Parsed JSON values do not match with class constructor
args=
arg types=
executable=Executable(Constructor(public scala.runtime.Nothing$()))
cause=null
types comparison result=
    at org.json4s.reflect.package$.fail(package.scala:95)
    at org.json4s.Extraction$ClassInstanceBuilder.instantiate(Extraction.scala:615)
    at org.json4s.Extraction$ClassInstanceBuilder.result(Extraction.scala:639)
    at org.json4s.Extraction$.$anonfun$extract$10(Extraction.scala:409)
    at org.json4s.Extraction$.$anonfun$customOrElse$1(Extraction.scala:646)
    ...

原因

scala的泛型类型信息,比如上述代码TransformToObj[Person]中的Person,在编译期时是存在的,但在jvm运行时TransformToObj中是得不到Person类型信息的。因此,对于jRead[A](json),由于编译器在编译时,从类型模板参数A获取不到真实的类型信息,所以编译报错。对于jRead(json),由于没有指定类型模板参数,虽然编译期时不会报错,但运行时还会由于获取不到真实类型信息,导致反序列化失败抛异常。

解决方法

明白了问题原因,那么解决的思路就比较明确了,即在编译时把消息case class的具体类型信息保留下来,并在调用TransformToObjtoObj方法时将类型信息传递给它。

方法1:参数传递类型信息

package io.neal.json

import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read => jRead}
import scala.reflect.Manifest

trait TransformToObj {
  def toObj[A](json: String)(mf: Manifest[A]): A = {
    implicit val format: AnyRef with Formats= Serialization.formats(NoTypeHints)
    jRead(json)(format, mf)
  }
}
package io.neal.json.message

import io.neal.json.{TransformToJson, TransformToObj}

case class Person(name: String, age: Int, email: String) extends TransformToJson
object Person extends TransformToObj {
  def toObj(json: String): Person = super.toObj[Person](json)(manifest[Person])
}

方法2:覆盖protected参数传递类型信息

package io.neal.json

import org.json4s._
import org.json4s.native.Serialization
import org.json4s.native.Serialization.{read => jRead}
import scala.reflect.Manifest

trait TransformToObj[A] {
  protected val mf: Manifest[A] = null

  def toObj(json: String): A = {
    implicit val format: AnyRef with Formats= Serialization.formats(NoTypeHints)
    jRead(json)(format, mf)
  }
}
package io.neal.json.message

import io.neal.json.{TransformToJson, TransformToObj}
import scala.reflect.Manifest

case class Person(name: String, age: Int, email: String) extends TransformToJson
object Person extends TransformToObj[Person] {
  override protected val mf: Manifest[Person] = manifest[Person]
}

相关文章

  • Scala 类型系统

    1.在scala泛型中获取其 Class[T] 需求:获取一个泛型 T 的 class 类型的 Class[T],...

  • Scala中泛型类型获取

    背景 笔者最近尝试使用case class作为ActiveMQ消息的定义。同时实现,在消息发送前,将case cl...

  • Scala泛型

    泛型的意思是 泛指某种具体的数据类型 , 在Scala中, 泛型用 [数据类型] 表示. 在实际开发中, 泛...

  • Scala泛型

    泛型类是以类型作为参数,Scala类型参数放在方括号[]中,Java放在<>中 变型 Variance Scala...

  • Java泛型的原始类型

    众所周知,Java中的泛型在编译期被擦除,那有没有办法在运行时获取到泛型的原始类型呢?有的。 获取泛型类型 如果定...

  • 通过反射获取泛型

    获取泛型参数的信息:1、通过Field.getGenericType()方法获取到Type类型(该对象中包含了泛型...

  • scala 泛型类型

    使用泛型类,通常是需要对类中的某些成员,比如某些field和method中的参数或变量,进行统一的类型限制,这样可...

  • Java 完美获取泛型类型(包含嵌套的泛型)

    完美获取泛型类型(包含嵌套多层的泛型) 1. 工具类 获取泛型类型工具类 2. 使用方法 2.1. 集成接口带有泛...

  • 好程序员大数据培训分享Scala系列之泛型

    好程序员大数据培训分享Scala系列之泛型,带有一个或多个类型参数的类是泛型的。 泛型类的定义: //带有类型参数...

  • java 泛型

    1,如何实例化泛型 2,如何获取java中的泛型类型 调用(通常在构造方法中调用):

网友评论

      本文标题:Scala中泛型类型获取

      本文链接:https://www.haomeiwen.com/subject/abvgxqtx.html