[Akka]:传递泛型类型函数,没有类型丢失

时间:2014-09-07 15:03:58

标签: scala akka

我有以下类型的演员消息:

case class RetrieveEntities[A](func:(Vector[A]) => Vector[A])

然后,我想以下列方式处理该消息:

def receive = {
    case RetrieveEntities(parameters, func) =>
          context.become(retrieveEntities(func))


def retrieveEntities(func:(Vector[T]) => Vector[T])(implicit mf: Manifest[T]){
    case _ => ...
}

我以下列方式实例化演员:

TestActorRef(new RetrieveEntitiesService[Picture]) 

问题是我收到以下编译器错误:

type mismatch;
[error]  found   : Vector[Any] => Vector[Any]
[error]  required: Vector[T] => Vector[T]
[error]       context.become(retrieveEntities(func))

我认为这意味着我丢失了类型信息,但我不确定为什么以及如何防止它。

2 个答案:

答案 0 :(得分:1)

您的示例代码有点简短,无法为您提供解决方案,但从您展示的内容来看,您似乎无法做到这一点。

这就是为什么

在Scala(和Java)中,类型参数被擦除,这意味着它们在编译后会消失,因此在运行时它们不再可用。这意味着RetrieveEntities(parameters, func)上的模式匹配实际上匹配A可以是任何内容。然后继续调用一个用T键入的方法,编译器无法知道你的意思。

Manifest(不推荐使用),TypeTagClassTag是一种机制,它告诉编译器创建一个对象,该对象为编译后的那些提供类型信息但是你必须&# 34;保存"那些信息。

为了能够知道你用A输入RetrieveEntitiesService你需要将一个隐式ClassTag带到构造函数中以使其基于任何逻辑(因为在调用构造函数时是时候你知道A是什么):

import scala.reflect.ClassTag
case class RetrieveEntities[A](func:(Vector[A]) => Vector[A])(implicit val tag: ClassTag[A])

然后,您可以在标记上调用runtimeClass以获取A:

的类型
scala> val retrieve = RetrieveEntities[String](identity)
scala> retrieve.tag.runtimeClass
res2: Class[_] = class java.lang.String

请注意,由于我们现在处于运行时,因此仍然不允许您键入方法调用,但它可以让您使用该Class实例与actor的E的runtimeClass进行比较,然后执行如果你愿意,可以安全地投射到RetrieveEntities[E]。 (还有常规的运行时条件流,反射等)。

开始之前的两个重要注意事项

我不建议你走这条路,直到你对类型系统更有信心,真的知道没有其他合理的设计可以解决你的问题。再次,我无法帮助您使用稀疏示例代码给出这样的解决方案。 (例如,你的演员可能并不真正需要知道A的类型,或者你可能会在具体类型上匹配一组有限的E

作为一个额外的警告,类型和类标签在Scala 2.10中不是线程安全的,也可能在2.11中不安全,因此将它们与actor混合可能是个坏主意。 (http://docs.scala-lang.org/overviews/reflection/thread-safety.html

答案 1 :(得分:0)

johanandren的回答肯定是有帮助的,但最后我找到了一种可以编译的方法,现在它正在运作。

我需要为编译器提供更精确的类型注释以使其工作:

case RetrieveEntities(parameters, func:(Vector[T]) => Vector[T])

我仍然会继续使用Manifest而不是新的Reflection APITypeTagClassTag),主要是因为我正在使用的库(json4s)在内部使用Manifest实现,我认为这样可以减少问题。