scalarookie scalarookie - 24 days ago 5
Scala Question

Preserving type arguments in Akka receive

This question has kind of been answered by Roland Kuhn in this post, however, despite several comments asking for detail, he didn't bother to share the complete answer.

Here's what I want to do: I have a wrapper class

case class Event[T](t: T)
of which I send instances to an Akka actor. In the
receive
method of that actor, I then want do distinguish between
Event[Int]
and
Event[String]
, which obviously isn't so simple due to type erasure.

What Roland Kuhn shares in the mentioned post is that "there is exactly one way to do it", that is, embodying the type information within the message. So I did this:

case class Event[T](t: T)(implicit val ct: ClassTag[T])


Even though asked by different people to provide it, Roland Kuhn does not say what to actually do within the
receive
method then. Here's what I tried.

def receive = {
case e: Event =>
if (e.ct.runtimeClass == classOf[Int])
println("Got an Event[Int]!")
else if (e.ct.runtimeClass == classOf[String])
println("Got an Event[String]!")
else
println("Got some other Event!")
case _ =>
println("Got no Event at all!")
}


This is the best I could come up with as it's hard to wrap one's head around Scala's reflection jungle. It's not compiling, though:

value ct is not a member of Any
else if (e.ct.runtimeClass == classOf[String])
^


Thus, I am asking specifically about what the
receive
method should look like.

Answer

After fixing the error Event takes type parameters:

def receive = {
  case e: Event[_] =>
    if (e.ct.runtimeClass == classOf[Int])
      println("Got an Event[Int]!")
    else if (e.ct.runtimeClass == classOf[String])
      println("Got an Event[String]!")
    else
      println("Got some other Event!")
  case _ =>
    println("Got no Event at all!")
}

the code compiles. It can be slightly simplified by not looking inside the ClassTags (of course, the implementation of ClassTag#equals is going to compare the classes):

import scala.reflect.{ClassTag, classTag}

def receive = {
  case e: Event[_] =>
    if (e.ct == ClassTag.Int) // or classTag[Int]
      println("Got an Event[Int]!")
    else if (e.ct == classTag[String])
      println("Got an Event[String]!")
    else
      println("Got some other Event!")
  case _ =>
    println("Got no Event at all!")
}