12

I am playing with this example http://scala.sygneca.com/code/remoteactors to learn how remote actors work in Scala (2.8.0). In particular I slightly modified how the messages send by the actors are defined as it follows:

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

and everything works as expected. Unfortunately if I define the events as case classes instead of case objects as in:

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

my example stop working. In more detail it seems that while case objects are serializable, case classes aren't. Indeed when I try to run my example with this last modification I get the following exception:

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)

Is there a reason why case objects can be made serializable and case classes can't? Is there a way to make my example working with case classes either?

Edit: as suggested by Victor and confirmed by Aaron I am sending the companion object as message instead of the class. Moreover inspecting the compiled code with javap it appears evident that while the class is serializable:

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

the companion object is not:

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

Now the question is: how can I specify that I want to use the class instead of the companion object? I also added an empty couple of parenthesis when I send the message as suggested by Aaron like in:

pong ! Ping()

but nothing is changed. In the end I also added a fake parameter to the case class

case class Ping(i: Int) extends Event

sending the message as:

pong ! Ping(0)

but without experiencing any difference still. Any suggestion?

Mario Fusco
  • 13,548
  • 3
  • 28
  • 37
  • 2
    As far as I know, you don't need to explicitly implement `Serializable` interface, as both case classes and case objects are already implicitly use `@serializable` (it's just sugared out). Compile your code with `scalac -print` to see the stripped code - it may help you to figure out what's the problem. A small test in the REPL shows that case classes can be [de-]serialized against 2.8.0. – Vasil Remeniuk Nov 01 '10 at 10:17
  • http://gist.github.com/657953 – Vasil Remeniuk Nov 01 '10 at 10:26
  • http://scala-programming-language.1934581.n4.nabble.com/deprecate-serializable-td3002109.html – Vasil Remeniuk Nov 01 '10 at 10:36

2 Answers2

15
@serializable case class Foo

I was also surprised that case objects were serializable per default.

Edit: After reading the exception properly I suspect that:

You're trying to send the generated companion object of the case class over the wire, instead of an instance of the case class.

Viktor Klang
  • 26,479
  • 7
  • 51
  • 68
  • 10
    Case classes are serializable by default too. Btw, in the snippets above case classes inherit from `Serializable`, which is in the most cases equal to using `@serializable` annotation – Vasil Remeniuk Nov 01 '10 at 12:43
  • 7
    In general, it's not recommended to use `@serializable`, and it will probably be deprecated soon: http://scala-programming-language.1934581.n4.nabble.com/deprecate-serializable-td3002109.html – Vasil Remeniuk Nov 01 '10 at 12:46
  • Sorry, but I think you are not responding to my question. The @serializable annotation and extending the Serializable interface should be almost the same (even if the first one is apparently going to be deprecated) Anyway I also tried to use the @serializable annotation and I experienced the same effect: case object can be made serializable while case classes can't. In other words even to write @serializable case class Ping doesn't work in my example. – Mario Fusco Nov 01 '10 at 15:52
  • 4
    To clarify Viktor's edit, it looks like you're calling `writeObject(Ping)` instead of `writeObject(Ping())`. – Aaron Novstrup Nov 01 '10 at 20:06
4

Case classes without parameters are meaningless and deprecated. And I see no Serializable in Scala, just serializable. Does it work if you fix these things?

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 2
    I wanted to use case classes exactly because I needed to add some parameters to them. The Serializable interface is actually the one in java.io package and using the @serializable annotation does the same and gives the same issue I experienced. Moreover, for what I know the @serializable annotation is going to be deprecated and this is the only reason why I was avoiding to use it. – Mario Fusco Nov 01 '10 at 16:00
  • @Mario I mean extending scala.serializable, which is the recommended way of doing it. – Daniel C. Sobral Nov 01 '10 at 20:39