【问题标题】:Why scala serializability differs in case classes with same constructor parameter types?为什么在具有相同构造函数参数类型的案例类中scala可序列化性不同?
【发布时间】:2016-12-25 17:47:02
【问题描述】:

为什么我可以序列化这个:

// Serialize: OK
case class ClassWithType2[T:TypeTag](x:T)  {
  val tpe:java.lang.reflect.Type = Util.toJavaClass[T]
}

...但不是这个

class TypeAware[T:TypeTag]() {
  val tpe:java.lang.reflect.Type = Util.toJavaClass[T]
}

// Serialize: FAIL.
// No valid constructor for ClassWithType1
// in: java.io.ObjectStreamClass.checkDeserialize
case class ClassWithType1[T:TypeTag](x:T) extends TypeAware[T] 

两者似乎具有相同的构造函数类型原型:

[T:TypeTag](x:T)

并且都扩展了 scala.Serializable 和 java.io.Serializable

val s1:Serializable = ClassWithType1(x=123)
val s2:Serializable = ClassWithType2(x=123)
val s3:java.io.Serializable = ClassWithType1(x=123)
val s4:java.io.Serializable = ClassWithType2(x=123)

有一种方法可以实现 TypeAware 子类:

  • 避免必须在每个子类中声明 tpe(如 ClassWithType2 所做的那样)?
  • 允许序列化对象

这是测试工具

class TypesTest {

  @Test
  def serializeTypeTest(): Unit = {
    val obj2:Object = ClassWithType2(x=123)
    Util.copyBySerialization(obj2)  // Success!

    val obj1:Object = ClassWithType1(x=123)
    Util.copyBySerialization(obj1) // Fail
  }
}

object Util {
  def toJavaClass[T:TypeTag]: Class[_] = {
    val tpe = typeOf[T]
    runtimeMirror(tpe.getClass.getClassLoader).runtimeClass(tpe.typeSymbol.asClass)
  }

  def copyBySerialization[T](obj: T): T = deserialize(serialize(obj))

  def serialize[T](obj: T): Array[Byte] = {
    val byteOut = new ByteArrayOutputStream()
    val objOut = new ObjectOutputStream(byteOut)
    objOut.writeObject(obj)
    objOut.close()
    byteOut.close()
    byteOut.toByteArray
  }

  def deserialize[T](bytes: Array[Byte]): T = {
    val byteIn = new ByteArrayInputStream(bytes)
    val objIn = new ObjectInputStream(byteIn)
    val obj = objIn.readObject().asInstanceOf[T]
    byteIn.close()
    objIn.close()
    obj
  }

}

【问题讨论】:

  • TypeAware 没有扩展 java.io.Serializable。
  • 为什么需要它?我要求序列化 ClassWithType1,它声称是可序列化的。注意。序列化失败是因为找不到 ClassWithType1 的构造函数,而不是因为无法序列化 TypeAware 或其成员(序列化 java.reflect.Type 工作正常)。
  • 每隔几年,我都会尝试刷新我对 Java 序列化的记忆,但我不认为今天是这一天。但是请看我的回答; ctor 不是无效的。

标签: scala generics serialization scala-generics


【解决方案1】:

仅引用 Javadoc:

为了允许序列化不可序列化类的子类型, 子类型可能承担保存和恢复状态的责任 超类型的公共、受保护和(如果可访问)包的 字段。只有当类 它扩展了一个可访问的无参数构造函数来初始化 类的状态。如果这样声明一个类 Serializable 是错误的 并非如此。将在运行时检测到错误。

TypeAware 的 ctor 包含隐式参数。

编辑:一个想法是使类型标签成为成员。或类似的。它没有节省太多语法。

abstract class TypeAware {
  protected def tt: TypeTag[_]
  def tpe:java.lang.reflect.Type = Util.toJavaClass(tt)
}

case class ClassWithType1[T](x:T)(implicit val tt: TypeTag[T]) extends TypeAware

编辑,更多 linx:

tech page

faq

your question

【讨论】:

  • 因此,使 TypeAware 扩展 Serializable 似乎通过向其子类添加缺少的构造函数来解决此问题。不是很直观,但我会接受。
  • 我想如果您必须使用它,值得阅读文档。还有一个关于决定是否需要标记特征的常见问题解答条目。
猜你喜欢
  • 1970-01-01
  • 2010-09-07
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多