支持Scala中List [(String,Any)]的通用反序列化

时间:2014-05-28 19:13:46

标签: scala reflection

这是对以下问题的跟进,其中涉及序列化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. 我创建的对象应该是不可变的case类对象,如果以通常的方式创建,它们将保证完全构造。但是,由于我在deserialize方法中改变了这些对象的实例字段,如果将对象发布给其他对象,我怎么能确定这些对象不会被视为部分构造(由于缓存和指令重新排序)线程?

1 个答案:

答案 0 :(得分:2)

  1. ClassTag的runtimeClass方法返回Class[_],而不是Class[T],可能是因为Scala和Java中的泛型表现不同;你可以尝试强行施放它:val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]

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

    • 寻找其他建设者

    • 编写自定义序列化程序(请参阅Gson如何做到这一点; BTW Gson只能自动序列化带有无参数构造函数的类以及它预定义反序列化器的类)

    • 对于案例类,查找其伴随对象并调用其apply方法

  3. 无论如何,反射也允许修改最终字段,所以如果你设法创建一个不可变对象,你就可以设置它的字段。