保留akka消息中的类型/类标记

时间:2014-10-21 05:39:47

标签: scala generics akka

我遇到这种情况,我希望保留有关消息中传递的某些泛型类型的信息,以便能够在负责处理消息的receive方法中创建具有相同类型的另一个泛型类。

乍一看,我认为TypeTag是我在这里最好的朋友,但是,在尝试完之后,似乎这不是最好的解决方案,或根本没有解决方案。让我先解释一下我目前的情况以及结果如何。

消息案例类

trait MessageTypeTag[T] {
  def typeTag: TypeTag[T]
}

case class Message[T](id: Int, payload: T, helper: MyClass[T], 
                      cond: Condition[MyClass[T]])(implicit val typeTag: TypeTag[T])
           extends MessageTypeTag[T]

MyClass2

class MyClass2[+T <: Any](_eval: Option[T] = None) {
  def getEval = _eval getOrElse None
}

接收方式

def receive() = {
  case m@Message(id, payload, helper, cond) => {
    // this prints a proper type tag, i.e. String, because type is known in the runtime
    println(m.typeTag.tpe)

    // compiler complains here because it sees m.typeTag as TypeTag[Any], i.e. exact
    // type is not known in the compile time
    val temp = new MyClass2[m.typeTag.tpe](...)
 }
}

肮脏的解决方案 在阅读了几篇关于Scala和akka的文章,讨论,文档之后,我通过放置(调用)工厂方法案例类来提出一些肮脏的解决方案。

case class Message[T](id: Int, payload: T, helper: MyClass[T], 
                      cond: Condition[MyClass[T]])(implicit val typeTag: TypeTag[T])
           extends MessageTypeTag[T] {
  def getMyClass2: MyClass2[T] = {
    // instantiate an object of type T
    val bla = typeTag.mirror.runtimeClass(typeTag.tpe).newInstance.asInstanceOf[T]
    // we can call apply here to populate created object or do whathever is needed
    ...
    // instantiate MyClass2 parametrized with type T and return it
    new MyClass2[T](Some(bla))
  }
}

正如你所看到的,这远不是好的解决方案/设计,因为这个案例类只是轻量级而且实际上违背了案例类本身的目的。它可以通过这里没有编码反射调用的方式进行改进,但是在一些外部工厂中进行改进,这种工厂只是在案例类中调用,但我觉得必须有更好的方法来实现这一点。

任何建议都将非常感谢。如果需要更多信息,我可以提供。

而且,我相信,类似的问题/解决方案已被描述here,但我想知道是否有更好的方法。感谢。

1 个答案:

答案 0 :(得分:1)

如果你想能够用反射来实例化一个类,那么你必须传递它,没有办法解决这个问题。我认为基于ClassTag的解决方案稍微简单一些:

val bla = classTag.runtimeClass.newInstance.asInstanceOf[T]

但它仍然非常难看。

将工厂作为一种功能传递而不是使用反射方法可能更好;这使您可以使用没有no-arg构造函数的类或需要一些设置的类:

case class Message[T](..., factory: () => T) {
  def getMyClass2 = new MyClass2[T](Some(factory()))
}

Message(..., {_ => new SomeTThatTakesArguments(3, 4)})

我怀疑最好的解决方案是更改您的MyClass2,以便它不会以相同的方式依赖于类型 - 也许您可以将约束MyClass2的需求表达为类型类您可以在Message中加入,也可以将其全部删除。但是,如果您希望我们在这些方面提出解决方案,您需要发布MyClass2