这是对以下问题的跟进,其中涉及序列化: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;
}
}
代码有几个问题:
val r: T = ...
行有一个编译错误,因为编译器认为它不能保证具有正确的类型。 (我通常不确定如何以类型安全的方式创建泛型类的新实例 - 不确定为什么这不安全,因为Serializer的实例是使用其类型被检查的类标记创建的由编译器)。答案 0 :(得分:2)
ClassTag的runtimeClass
方法返回Class[_]
,而不是Class[T]
,可能是因为Scala和Java中的泛型表现不同;你可以尝试强行施放它:val r: T = ctag.runtimeClass.newInstance.asInstanceOf[T]
newInstance
调用默认的无参数构造函数。如果该类没有,newInstance
将抛出InstantiationException
。没有办法绕过它,除了:
寻找其他建设者
编写自定义序列化程序(请参阅Gson如何做到这一点; BTW Gson只能自动序列化带有无参数构造函数的类以及它预定义反序列化器的类)
对于案例类,查找其伴随对象并调用其apply
方法
无论如何,反射也允许修改最终字段,所以如果你设法创建一个不可变对象,你就可以设置它的字段。