【问题标题】:Support generic deserialization from a List[(String, Any)] in Scala支持 Scala 中 List[(String, Any)] 的通用反序列化
【发布时间】:2014-07-18 04:09:21
【问题描述】:

这是对以下问题的跟进,涉及序列化How best to keep a cached list of member fields, one each for a family of case classes in Scala

我正在尝试以同样的方式普遍支持反序列化。一种直接的尝试如下:

  abstract class Serializer[T](implicit ctag: ClassTag[T]) {
    private val fields = ctag.runtimeClass.getDeclaredFields.toList
    fields foreach { _.setAccessible(true) }
    implicit class AddSerializeMethod(obj: T) {
      def serialize = fields.map(f => (f.getName, f.get(obj)))
    }
    def deserialize(data: List[(String, Any)]): T = {
      val m = data toMap
      val r: T = ctag.runtimeClass.newInstance // ???
      fields.foreach { case f => f.set(r, m(f.getName)) }
      r;
    }
  }

代码有几个问题:

  1. val r: T = ... 的行存在编译错误,因为编译器认为不能保证它具有正确的类型。 (我通常不确定如何以类型安全的方式创建泛型类的新实例——不确定为什么这不安全,因为 Serializer 的实例是使用编译器检查其类型的类标记创建的) .
  2. 我正在创建的对象应该是不可变的案例类对象,如果以通常的方式创建,则可以保证它们是完全构造的。但是,由于我在反序列化方法中对这些对象的实例字段进行了变异,如果将这些对象发布到其他线程,我如何确保它们不会被视为部分构造(由于缓存和指令重新排序)?

【问题讨论】:

  • 这样做并不安全,但对于#1,您可以通过以下方式解决此问题:val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]。不过,我想不出这样不安全的情况。

标签: scala reflection


【解决方案1】:
  1. ClassTag 的runtimeClass 方法返回Class[_],而不是Class[T],可能是因为Scala 和Java 中的泛型行为不同;您可以尝试强制投射:val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]

  2. newInstance 调用默认的无参数构造函数。如果类没有,newInstance 将抛出 InstantiationException。没有办法解决它,除了:

    • 四处寻找其他构造函数

    • 编写自定义序列化器(看看 Gson 是如何做到的;顺便说一句,Gson 只能自动序列化具有无参数构造函数的类以及它已为其预定义反序列化器的类)

    • 对于案例类,找到它们的伴生对象并调用其apply 方法

无论如何,反射也允许修改最终字段,因此如果您设法创建一个不可变对象,您将能够设置其字段。

【讨论】:

  • 关于可变性,是的,我知道我可以修改最终字段。我担心这样做的安全性。例如,如果我调用deserialize,然后将生成的对象发布到不同的线程,并且该线程读取对象字段,是否保证看到调用deserialize 的原始线程在调用@987654332 后看到的相同状态@ 或者它是否可能会看到陈旧的、未初始化的数据,而不是由于处理器缓存中的陈旧。
  • @jonderry 我不知道,但这表明一个空的同步块可能就足够了:stackoverflow.com/a/696216/1648987
猜你喜欢
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 2016-08-18
  • 1970-01-01
  • 2014-02-08
  • 2011-11-11
  • 1970-01-01
  • 2018-01-09
相关资源
最近更新 更多