为啥案例对象可序列化而案例类不可序列化?

Posted

技术标签:

【中文标题】为啥案例对象可序列化而案例类不可序列化?【英文标题】:Why are case objects serializable and case classes not?为什么案例对象可序列化而案例类不可序列化? 【发布时间】:2011-05-03 08:09:07 【问题描述】:

我正在使用这个示例http://scala.sygneca.com/code/remoteactors 来了解远程演员如何在 Scala (2.8.0) 中工作。特别是,我稍微修改了参与者发送的消息的定义方式,如下所示:

sealed trait Event extends Serializable
case object Ping extends Event
case object Pong extends Event
case object Quit extends Event

一切都按预期进行。不幸的是,如果我将事件定义为案例类而不是案例对象,如下所示:

sealed trait Event extends Serializable
case class Ping extends Event
case class Pong extends Event
case class Quit extends Event

我的示例停止工作。更详细地说,虽然案例对象是可序列化的,但案例类不是。事实上,当我尝试使用最后一次修改运行我的示例时,我得到了以下异常:

scala.actors.remote.DelegateActor@148cc8c: caught java.io.NotSerializableException: scalachat.remote.Ping$
java.io.NotSerializableException: scalachat.remote.Ping$
    at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1156)
    at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)
    at scala.actors.remote.JavaSerializer.serialize(JavaSerializer.scala:46)
    at scala.actors.remote.NetKernel.namedSend(NetKernel.scala:38)
    at scala.actors.remote.NetKernel.forward(NetKernel.scala:71)
    at scala.actors.remote.DelegateActor$$anonfun$act$1$$anonfun$apply$1.apply(Proxy.scala:182)
    at scala.actors.remote.DelegateActor$$anonfun$act$1$$anonfun$apply$1.apply(Proxy.scala:123)
    at scala.actors.ReactorTask.run(ReactorTask.scala:34)
    at scala.actors.ReactorTask.compute(ReactorTask.scala:66)
    at scala.concurrent.forkjoin.RecursiveAction.exec(RecursiveAction.java:147)
    at scala.concurrent.forkjoin.ForkJoinTask.quietlyExec(ForkJoinTask.java:422)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.mainLoop(ForkJoinWorkerThread.java:340)
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:325)

案例对象可以序列化而案例类不能序列化有什么原因吗?有没有办法让我的示例与案例类一起使用?

编辑:正如 Victor 所建议并由 Aaron 确认的那样,我将伴随对象作为消息而不是类发送。此外,使用 javap 检查编译后的代码很明显,虽然该类是可序列化的:

public class scalachat.remote.Ping extends java.lang.Object implements scalachat.remote.Event,java.io.Serializable,scala.ScalaObject,scala.Product

伴生对象不是:

public final class scalachat.remote.Ping$ extends scala.runtime.AbstractFunction0 implements scala.ScalaObject

现在的问题是:如何指定我想使用类而不是伴随对象?当我按照 Aaron 的建议发送消息时,我还添加了一对空括号,例如:

pong ! Ping()

但没有任何改变。最后我还给case类加了一个fake参数

case class Ping(i: Int) extends Event

将消息发送为:

pong ! Ping(0)

但仍然没有任何区别。有什么建议吗?

【问题讨论】:

据我所知,您不需要显式实现Serializable 接口,因为案例类和案例对象都已经隐式使用@serializable(它只是糖化了)。使用scalac -print 编译您的代码以查看剥离的代码 - 它可以帮助您找出问题所在。 REPL 中的一个小测试表明案例类可以针对 2.8.0 [反]序列化。 gist.github.com/657953 scala-programming-language.1934581.n4.nabble.com/… 【参考方案1】:
@serializable case class Foo

我也很惊讶 case 对象在默认情况下是可序列化的。

编辑:正确阅读异常后,我怀疑:

您正在尝试通过线路发送生成的案例类的伴随对象,而不是案例类的实例。

【讨论】:

案例类默认也是可序列化的。顺便说一句,在上面的 sn-ps 案例中,类继承自 Serializable,这在大多数情况下等于使用 @serializable 注释 一般情况下不建议使用@serializable,可能很快就会被弃用:scala-programming-language.1934581.n4.nabble.com/… 抱歉,我认为您没有回答我的问题。 @serializable 注释和扩展 Serializable 接口应该几乎相同(即使第一个显然会被弃用)无论如何我也尝试使用 @serializable 注释并且我遇到了相同的效果:case object can be made serializable而案例类不能。换句话说,即使编写@serializable case class Ping 在我的示例中也不起作用。 为了澄清 Viktor 的编辑,您似乎在调用 writeObject(Ping) 而不是 writeObject(Ping())【参考方案2】:

没有参数的案例类是没有意义的并且已被弃用。我在 Scala 中看不到 Serializable,只有 serializable。如果你解决这些问题,它会起作用吗?

【讨论】:

我想使用案例类正是因为我需要向它们添加一些参数。 Serializable 接口实际上是 java.io 包中的接口,使用 @serializable 注释的作用相同,并给出了我遇到的相同问题。此外,据我所知,@serializable 注释将被弃用,这是我避免使用它的唯一原因。 @Mario 我的意思是扩展 scala.serializable,这是推荐的方式。

以上是关于为啥案例对象可序列化而案例类不可序列化?的主要内容,如果未能解决你的问题,请参考以下文章

JSON将Scala案例类序列化为仅字符串和整数

关于序列化和反序列化案例看这一篇就够用了,简直讲的清新脱俗!

为啥我不应该让一个类可序列化?

为啥 MicroBatchReader 必须是可序列化的?任务不可序列化错误

使用 Jackson 来(反)序列化 Scala 案例类

从零开始的Java开发1-6-4 Java输入输出流:File类绝对路径和相对路径字节流缓冲流字符流对象序列化