【问题标题】:Scala serialization/deserialization of singleton object单例对象的Scala序列化/反序列化
【发布时间】:2012-11-23 01:36:02
【问题描述】:

我对 scala 编程语言很陌生,我目前需要做以下事情。我有一个如下的 signleton 对象:

object MyObject extends Serializable {
    val map: HashMap[String, Int] = null
    val x: int = -1;
    val foo: String = ""
}

现在我想避免必须单独序列化这个对象的每个字段,因此我正在考虑将整个对象写入一个文件,然后在下一次执行程序时读取文件并初始化单例对象从那里。有没有办法做到这一点?

基本上我想要的是当序列化文件不存在时,将这些变量初始化为新结构,而当它存在时,从文件中的字段初始化字段。但我想避免手动序列化/反序列化每个字段...

更新:

我必须使用此处介绍的自定义反序列化器:https://issues.scala-lang.org/browse/SI-2403,因为我在 HashMap 中作为值使用的自定义类存在问题。

更新2:

这是我用来序列化的代码:

val store = new ObjectOutputStream(new FileOutputStream(new File("foo"))) 
store.writeObject(MyData) 
store.close

以及要反序列化的代码(在不同的文件中):

@transient private lazy val loadedData: MyTrait = {
    if(new File("foo").exists()) {
        val in = new ObjectInputStream(new FileInputStream("foo")) {
            override def resolveClass(desc: java.io.ObjectStreamClass): Class[_] = {
                try { Class.forName(desc.getName, false, getClass.getClassLoader) }
                catch { case ex: ClassNotFoundException => super.resolveClass(desc) }
            }
        }
        val obj = in.readObject().asInstanceOf[MyTrait] 
        in.close
        obj
    }
    else null
}

谢谢,

【问题讨论】:

  • 我们很乐意看到这个问题:)
  • 在这里 :) 出于某种原因,它在我写完之前就发布了......
  • 出于好奇,这是干什么用的?读取配置文件,也许?
  • 基本上,我正在开发一个系统,出于持久性原因,我希望将其主要数据结构写入文件。数据结构将(最终:))变得非常大,这就是我想避免必须单独序列化每个字段的原因。
  • 旁注:避免在 Scala 中使用 null。它实际上只存在于与 Java 的互操作性。如果某些内容可以为“空”,请改用Option

标签: scala serialization singleton deserialization


【解决方案1】:

不需要序列化只有不可变字段的对象(因为编译器会为你做这件事......)我会假设该对象提供默认值。这是一种方法:

首先编写一个包含所有必填字段的特征:

trait MyTrait {
  def map: HashMap[String, Int]
  def x: Int
  def foo: String
}

然后用默认值写一个对象:

object MyDefaults extends MyTrait {
  val map = Map()
  val x = -1
  val foo = 
}

如果存在,最后编写一个反序列化数据的实现:

object MyData extends MyTrait {

  private lazy val loadedData: Option[MyTrait] = {
     if( /* filename exists */ ) Some( /*unserialize filename as MyTrait*/)
    else None
  }
  lazy val map = loadedData.getOrElse( MyDefault ).map
  lazy val x = loadedData.getOrElse( MyDefault ).x
  lazy val foo = loadedData.getOrElse( MyDefault ).foo

}

【讨论】:

  • 好的。我明白你的意思。但是有没有办法不使用工厂方法?例如我记得每当引用 MyDefaults 时,都会调用类似于您的 apply 方法的东西。这可能吗?
  • 我在没有工厂的情况下重写了答案(使用延迟加载变量)。
  • 抱歉耽搁了。我了解您的解决方案,但是,如果我做的一切正确,它会抛出 java.lang.StackOverflowError。我想当我反序列化时,我以某种方式一次又一次地调用相同的初始化......但我不知道为什么! (请注意,我必须更改 getOrElse 和 Option,因为我将编译错误更改为简单的 if 条件,并且我必须使用自定义反序列化器 - 有关后者的更多详细信息,请参阅我的帖子中的编辑)
  • @Y.K. 1. 我刚刚修正了一个小错字(getOrEsle 而不是getOrElse)。带有选项的代码现在必须工作,如果不显示错误消息。 2.如果你直接序列化MyData使用Serializable你应该将loadedData字段标记为@transient以避免无限递归序列化。
  • 你好。谢谢回复。 getOrElse 报错如下:``value map is not a member of Option[MyTrait]''。即使我将loadedData标记为瞬态,我仍然会得到无限递归。我现在将在我的原始帖子中添加有关如何序列化和反序列化的代码示例。如果您有任何其他想法,请告诉我:-)。谢谢!
猜你喜欢
  • 1970-01-01
  • 2017-08-02
  • 1970-01-01
  • 2015-11-14
  • 1970-01-01
  • 2014-08-18
  • 2020-04-26
  • 2013-08-12
  • 1970-01-01
相关资源
最近更新 更多