在这种情况下,我希望保留消息中传递的某个泛型类型的信息,以便能够在负责处理消息的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))
}
}
正如您所看到的,这远远不是一个好的解决方案/设计,因为这个case类几乎是轻量级的,实际上违背了case类本身的目的。它可以通过以下方式进行改进:反射调用不在这里编码,而是在一些外部工厂中编码,这些工厂只是在case类中调用,但我觉得必须有更好的方法来实现这一点。
任何建议都将不胜感激。如果需要更多的信息,我可以提供
而且,我相信这里已经描述了类似的问题/解决方案,但我想知道是否有更好的方法。谢谢
如果你想用反射实例化一个类,那么你必须传递它,这是没有办法的。我认为基于ClassTag
的解决方案稍微简单一点:
val bla = classTag.runtimeClass.newInstance.asInstanceOf[T]
但它还是很难看。
与其使用反思的方法,不如将工厂作为一种功能进行传递;这使您可以处理没有arg构造函数或需要一些设置的类:
case class Message[T](..., factory: () => T) {
def getMyClass2 = new MyClass2[T](Some(factory()))
}
Message(..., {_ => new SomeTThatTakesArguments(3, 4)})
我怀疑最好的解决方案是更改MyClass2
,使其不以同样的方式依赖于类型——也许您可以将约束MyClass2
表示为一个类型类,您可以将其包含在Message
中,或者完全忽略它。但如果你想让我们就这些问题提出解决方案,你需要发布MyClass2
。