Akka Actor - 按通用类型接收模式匹配

Akka Actor - Receive Pattern Match by Generic Type

我有一个通用 actor class,我想在接收方法中与通用类型进行模式匹配。

例如:

class Test[S] extends Actor {
  override def receive: Receive = {
    case _: S => println("yes")
    case _ => println("no")
  }
}

val ac = system.actorOf(Props(new Test[Int]))
ac ! "abc" // expect print "no" 
ac ! 3     // expect print "yes"

然而,似乎它永远不会进入第二种情况,并且总是打印输出"yes"...

如何让它打印 "no"?

问题

您刚刚偶然发现了一个叫做 type erasure 的东西。这意味着,泛型类型信息在编译期间被删除(JVM 不了解泛型)。所以在运行时,你的演员将看起来像这样:

class Test extends Actor {
  override def receive: Receive = {
    case _: Object => println("yes")
    case _ => println("no")
  }
}

这就是为什么 ac ! "abc"ac ! 3 总是匹配第一种情况。

幸运的是,Scala 提供了有助于克服这个问题的机制。您可以阅读更多关于它们的信息 in this article and in the docs.

解决方案

tldr;您可以找到一个工作示例 here, on Scastie.

您应该使用其中一种机制:ClassTag。您可以将其视为一个对象,它包含在运行时编译时可用的所有信息。

import scala.reflect.ClassTag

object Main extends App {

  class Test[S](implicit ct: ClassTag[S]) extends Actor {
    override def receive: Receive = {
      case _: S => println("yes")
      case _ => println("no")
    }
  }

  val system = ActorSystem()

  val ac = system.actorOf(Props(new Test[Int]))
  ac ! "abc" // expect print "no"
  ac ! 3 // expect print "yes"

  system.terminate()

}

您可能想知道我们在哪里可以找到 ClassTag[T] 的隐式实例。还好编译器会按需提供

奖励:您可以重写 class Test[S](implicit ct: ClassTag[S]) extends Actor 以使用所谓的 Context Boundsclass Test[S: ClassTag] extends Actor.

您可以通过以下方式阅读此签名:

A class Test is parametrized by type S, for which there is available ClassTag[S] in an implicit scope.