Serialization of Unit in akka
A common mistake in scala is using Unit
, the companion object, as its value. The correct value of type Unit
is ()
. Typically, one can get away with using Unit
as the value, and
def unitType: Unit = Unit
def unitValue: Unit = ()
behave similarly. However, this becomes an issue when serialization1 comes into play. Take, for example, the following actor2:
class UnitActor extends Actor {
override def receive: Receive = {
case Unit => sender ! "Received Unit Object"
case () => sender ! "Received Unit value"
}
}
This simply returns a string to the sender upon receipt of a message. When run on the same VM, everything behaves as expected:
class NoSerializableTest extends TestKit(ActorSystem("SerializableTest")) with WordSpecLike with ImplicitSender {
"ActorSystem without requiring serialization" should {
val unitActor = TestActorRef[UnitActor]
"send Unit object" in {
unitActor ! Unit
within(1 second){
expectMsg("Received Unit Object")
}
}
"send unit value" in {
unitActor ! ()
within(1 second){
expectMsg("Received Unit value")
}
}
}
}
However, if the same actor was used in a distributed system, messages would have to be serialized and Unit
messages will not be sent across the wire.
class SerializableTest extends TestKit(ActorSystem("SerializableTest", ConfigFactory.parseString(
"akka.actor.serialize-messages = on"))) with WordSpecLike with ImplicitSender {
"ActorSystem with serialization" should {
val unitActor = TestActorRef[UnitActor]
"fail to send Unit Object" in {
unitActor ! Unit
within(1 second){
expectNoMsg()
}
}
"send unit value" in {
unitActor ! ()
within(1 second){
expectMsg("Received Unit value")
}
}
}
}
Note that we have mimicked what would happen when messages are sent between VMs by setting akka.actor.serialize-messages = on
3. Using the default java serializer, ()
messages are sent without a problem, but Unit
messages are dropped with the following error being logged to STDOUT:
[ERROR] [05/17/2015 14:37:30.959] [pool-6-thread-3-ScalaTest-running-SerializableTest] [akka://SerializableTest/user/$$a] swallowing exception during message send
java.io.NotSerializableException: No configured serialization-bindings for class [scala.Unit$]
Unit
is just a regular companion object and plain objects are not serializable, only case objects
are.
Take aways:
- Use
()
as theUnit
value and notUnit
itself. - Turn on
serialize-messages
when testing actor systems that will leave a single VM.