【问题标题】:is it possible to overcome type erasure in akka receive?是否有可能克服 akka 接收中的类型擦除?
【发布时间】:2013-12-23 07:42:09
【问题描述】:

我有一个 trait 并实现了 akka actor,由于类型擦除,无法正确区分传入消息的类型,因此第一种情况是匹配所有消息。

我正在使用 scala 2.10.x,通过阅读许多其他答案,我知道可以使用 TypeTagClassTag 恢复特征内的类型信息,但我不知道如何应用它(如果可能的话)在 akka 中接收。

我的(非常简化的)示例如下。是否可以正确匹配泛型类型?

package com.ebay.box.canada.batch.jobs.siteMap

import akka.actor.Actor
import akka.actor.ActorSelection
import akka.actor.Actor.Receive
import scala.reflect.ClassTag


trait MessageProcessor[A,B] {
  this: Actor =>

  val destA: ActorSelection
  val destB: ActorSelection

  def processA(a: A): A
  def processB(a: B): B

  def receive: PartialFunction[Any,Unit] = {
    case a: A =>
      destA ! processA(a)
    case b: B =>
      destB ! processB(b)
  }
}

class StringIntProcessor(val destA: ActorSelection, val destB: ActorSelection) extends MessageProcessor[String,Int] with Actor {
  def processA(a: String) = { a + "1" }
  def processB(b: Int) = { b + 1 }
}

【问题讨论】:

  • 不应该def process(a: B): Bdef process(b: B): B
  • @ElliottFrisch,我已经编辑了示例代码。从技术上讲,差异对编译器或手头的问题无关紧要。不过,从风格上讲,这不是我的本意,而且命名应该是一致的。

标签: scala akka type-erasure


【解决方案1】:

我不认为你可以在你的 trait 中找到 TypeTag[A]ClassTag[A] —— 类型标签/类标签始终是方法调用的隐式参数列表的一部分。您也许可以使用带有隐式构造函数参数的抽象类:

import scala.reflect.runtime.universe._

abstract class MessageProcessor[A,B]()(implicit cta: ClassTag[A], ctb: ClassTag[B]) {
   def receive = {
     case a: Any if a.getClass() == cta.runtimeClass =>
       process(a.asInstanceOf[A])
     ...
   }
   ...
}

(未测试!)

假设您可以更改发送消息的代码,我可以建议以下设计吗? MessageProcessor 现在是一个类型类,因此您可以添加任意数量的消息类型。通过将闭包作为消息发送,您可以将任意数量的上下文偷运到调用站点。

class MessageReceiver extends Actor {
  def receive = {
    case fn: Function0[Unit] =>
      fn()
  }
}

trait MessageProcessor[A] {
  val dest: ActorSelection
   def process(a: A): A
}

object Processors {

  implicit object StringProcessor extends MessageProcessor[String] {
    val dest: ActorSelection = Wellknown.stringDest
    def process(a: String): String = a + "1"
  }

  implicit object IntProcessor extends MessageProcessor[Int] {
    val dest: ActorSelection = Wellknown.intDest
    def process(a: Int): Int = a + 1
  }

  def sendMessage[A](msg: A)(implicit ev:[MessageProcessor[A]]): Unit = {
    val block: Function0[Unit] = { () =>
      ev.dest ! ev.process(msg)
    }

    val msgDest = system.actorOf[Props[MessageReceiver]], "msgDest")
    msgDest ! block
  }
}

(也未测试!)

【讨论】:

  • 隐式 ClassTag 是否必须在构造上——它不能是隐式 val 或接收 PartialFunction 上的隐式?
  • 很确定它不能——receive 的类型是PartialFunction[Any, Unit],你不能将它更改为具有隐式参数列表。无论如何,这是您需要捕获的 A 和 B 的类型,这必须在 MessageProcessor 被具体化时发生。 (即提供具体类型时)。
  • 啊,好吧..所以我想我明白为什么receive 不能更改,因为我在实现它时无法更改方法签名(让你想知道为什么akka 人没有' t 自己添加一个隐式参数,因为类匹配对参与者来说非常重要),但是在具体类的构造函数设置的代码主体中使用 val 不是这种情况吗? (类似于我在示例中定义 destAdestB 的方式)
  • 实际上,在您的设计中,擦除类型就足够了——类型是StringInt。 TypeTags 可能有助于区分例如Option[String]Option[Int],其中擦除的类型只是 Option[_]。不过,TypeTags 的出现晚于 Akka。我的MessageProcessor trait 的目的是将类型、目的地和处理方法联系在一起。您应该查看类型类,这是我在这里使用的技术。
  • 我必须稍微编辑一下代码才能使其正确编译(process 方法实际上是重复的,同样是因为类型擦除),但是当我编译它时,我得到了警告type pattern is unchecked since it is eliminated by erasure,所以当你说擦除类型就足够时,我不确定你的意思是什么?看来编译器无法区分 A 和 B,因为它们都是通用的,都被擦除(我猜)到 Any。我将研究您使用的“类型类”,以了解如何将它们应用于我的更大问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-30
  • 1970-01-01
  • 2016-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多