Scala Akka - generics type in receive handler

2019-08-09 05:36发布

问题:

I am trying to get my head around on what is best way to code this implementation. To give you example, here is my DAO handler code looks like

trait IDAOHandler[+T]  {
  def create[U <: AnyRef: Manifest](content: U): Try[String]
}

class MongoDAOHAndler extends IDAOHandler[+T]...

So I am creating actor that will handle all my persistence task that includes serializing the content and updating MongoDB database.

So I am using akka and the trick is in receive method, how do i handle generics type parameter. Even though my actor code is non-generic, but the messages it is going to receive will be generic type and based on content type in createDAO I was planning to get appropriate DAO handler (described aboe) and invoke the method.

case class createDAO[T](content: T) (implicit val metaInfo:TypeTag[T])

class CDAOActor(daofactory: DAOFactory) extends BaseActor {
  def wrappedReceive = {
    case x: createDAO[_] => pmatch(x)
  }

  def pmatch[A](c: createDAO[A]) {
     //getting dao handler which will not work because it needs manifest
  }
}

Let me know if there are any other ways to re-write this implementation.

回答1:

You might already know this, but a little background just to be sure: In Scala (and Java) we have what is called type erasure, this means that the parametric types are used to verify the correctness of the code during compile time but is then removed (and "does not give a runtime cost", http://docs.oracle.com/javase/tutorial/java/generics/erasure.html). Pattern matching happens during runtime so the parametric types are already erased.

The good news is that you can make the Scala compiler keep the erased type by using TypeTag like you have done in your case class or ClassTag which contains less information but also keeps the erased type. You can get the erased type from the method .erasure (.runtimeClass in Scala 2.11) which will return the Java Class of the T type. You still wont be able to use that as the type parameter for a method call as that again happens compile time and you are now looking at that type in runtime, but what you can do is to compare this type during runtime with if/else or patternmatching.

So for example you could implement a method on your daofactory that takes a Class[_] parameter and returns a DAO instance for that class. In pmatch you would then take the erased type out of the tag and pass on to it.

Here is some more info about the tags, why they exist and how they work: http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html



回答2:

I took a bit different approach, kind of dispatcher pattern, so here is the revised code

trait IDAOProcess 
{
  def process(daofactory:IDAOFactory,sender:ActorRef) 
}

case class createDAO[T <: AnyRef : Manifest](content:T) (implicit val metaInfo:TypeTag[T]) extends IDAOProcess
{
  def process(daofactory:IDAOFactory,sender:ActorRef)
  {
    for ( handler <- daofactory.getDAO[T] ) 
    {
      handler.create(content)
    }
  }
}

class DAOActor(daofactory:IDAOFactory) extends BaseActor  
{
  def wrappedReceive =
  {
    case x:IDAOProcess => 
      {
        x.process(daofactory,sender)
      }
  }
}


标签: scala akka