支持从 Scala 中的 List[(String, Any)] 进行通用反序列化 [英] Support generic deserialization from a List[(String, Any)] in Scala

查看:35
本文介绍了支持从 Scala 中的 List[(String, Any)] 进行通用反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是对以下问题的跟进,该问题涉及序列化:如何最好地保留成员字段的缓存列表,每个用于Scala 中的一系列案例类

This is a follow up to the following question, which concerned serialization: How best to keep a cached list of member fields, one each for a family of case classes in Scala

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

I'm trying to generically support deserialization in the same way. One straightforward attempt is the following:

  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 类对象,如果以通常的方式创建,则保证它们是完全构造的.但是,由于我在反序列化方法中改变了这些对象的实例的字段,如果它们被发布到其他线程,我如何确保这些对象不会被视为部分构造(由于缓存和指令重新排序)?
  1. The line with val r: T = ... has a compile error because the compiler thinks it's not guaranteed to have the right type. (I'm generally unsure of how to create a new instance of a generic class in a typesafe way -- not sure why this isn't safe since the instance of Serializer is created with a class tag whose type is checked by the compiler).
  2. The objects I'm creating are expected to be immutable case class objects, which are guaranteed to be fully constructed if created in the usual way. However, since I'm mutating the fields of instances of these objects in the deserialize method, how can I be sure that the objects will not be seen as partially constructed (due to caching and instruction reordering) if they are published to other threads?

推荐答案

  1. ClassTag 的 runtimeClass 方法返回 Class[_],而不是 Class[T],可能是由于 Scala 中的泛型和 Java 的行为不同;你可以尝试强制转换:val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]

  1. ClassTag's runtimeClass method returns Class[_], not Class[T], probably due to the fact generics in Scala and Java behave differently; you can try casting it forcefully: val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]

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

newInstance calls the default, parameterless constructor. If the class doesn't have one, newInstance will throw InstantiationException. There's no way around it, except for:

  • 四处寻找其他构造函数

  • looking around for other constructors

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

writing custom serializers (see how Gson does that; BTW Gson can automatically serialize only classes with parameterless constructors and those classes it has predefined deserializers for)

对于 case 类,找到它们的伴生对象并调用它的 apply 方法

for case classes, finding their companion object and calling its apply method

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

Anyhow, reflection allows for modifying final fields as well, so if you manage to create an immutable object, you'll be able to set its fields.

这篇关于支持从 Scala 中的 List[(String, Any)] 进行通用反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆