我使用Scala 2.10.4和akka 2.3.4。我遇到了类型推断不符合我预期的问题。
下面的代码说明了我遇到的一个例子。我有一个案例类,它使用名为id
的{{1}}来包装邮件。它使用消息类型进行参数化。然后我有一个名为MyMessage
的有效负载,其中包含MyPayload
。
在一个actor中(这里我只是使用一个名为String
的常规对象,因为问题不是akka特有的)我是模式匹配并调用一个对我的有效负载类型进行操作的函数MyObject
。
MyPayload
由于我不明白的原因,使用package so
case class MyMessage[T](id:Long, payload:T)
case class MyPayload(s:String)
object MyObject {
def receive:PartialFunction[Any, Unit] = {
case m @ MyMessage(id, MyPayload(s)) =>
// Doesn't compile
processPayload(m)
// Compiles
processPayload(MyMessage(id, MyPayload(s)))
}
def processPayload(m:MyMessage[MyPayload]) = {
println(m)
}
}
和未应用的案例类进行模式修补并不能推断@
的类型参数。在上面的代码中,我原以为MyMessage[T]
的类型为m
。但是,当我编译时,它认为类型是MyMessage[MyPayload]
。
MyMessage[Any]
这是预期的行为吗?如果是这样,我对Scala中的类型推断有什么误解?
答案 0 :(得分:5)
您无法在模式匹配中提取类型参数 - 它是当前实现和/或运行时的限制。因为类型参数在运行时被擦除,所以需要很多开销来恢复它们 - 因此你不能使用在模式匹配中采用类型参数的unapply
方法。
在您的情况下,它看起来更简单,因为编译器只能从提取器参数推断出类型。但一般来说,这并不容易,也可能就是为什么它甚至不适合你的情况。
有关此问题,请参阅this long living ticket。
答案 1 :(得分:2)
您遇到的问题是类型删除
JVM在运行时期间对通用类型一无所知,请参阅:
How do I get around type erasure on Scala? Or, why can't I get the type parameter of my collections?
要使其编译,你必须详细告诉编译你期望的类型
def receive:PartialFunction[Any, Unit] = {
case message: MyMessage[MyPayload] =>
processPayload(message)
}
警告:这仍然会匹配任何MyMessage [_] ,并可能导致运行时异常。
要在运行时确保类型,您需要使用TypeTags(请参阅上面的链接)
答案 2 :(得分:0)
奇怪的是它也适用于:
def receive: PartialFunction[Any, Unit] = {
case m : MyMessage[MyPayload] => processPayload(m)
}
scala> MyObject.receive.isDefinedAt(MyMessage(12L, MyPayload("string")))
res9: Boolean = true