简体   繁体   English

使用TypeTag克服Scala中的类型擦除

[英]Overcoming Type Erasure in Scala with TypeTag

I have the following case class: 我有以下案例类:

trait Event
object Event {
  case class ProducerStreamActivated[T <: KafkaMessage](kafkaTopic: String, stream: SourceQueueWithComplete[T]) extends Event
}

trait KafkaMessage
object KafkaMessage {

  case class DefaultMessage(message: String, timestamp: DateTime) extends KafkaMessage {
    def this() = this("DEFAULT-EMPTY-MESSAGE", DateTime.now(DateTimeZone.UTC))
  }

  case class DefaultMessageBundle(messages: Seq[DefaultMessage], timeStamp: DateTime) extends KafkaMessage {
    def this() = this(Seq.empty, DateTime.now(DateTimeZone.UTC))
  }
}

In one of my Actor, I have the following method that identifies the actual Type: 在我的一个Actor中,我可以使用以下方法来标识实际的Type:

class KafkaPublisher[T <: KafkaMessage: TypeTag] extends Actor {

  def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
    val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
    println(s"type of $x has type arguments $targs")
  }

  implicit val system = context.system
  val log = Logging(system, this.getClass.getName)

  override final def receive = {
    case ProducerStreamActivated(_, stream) =>
      paramInfo(stream)
      log.info(s"Activated stream for Kafka Producer with ActorName >> ${self.path.name} << ActorPath >> ${self.path} <<")
      context.become(active(stream))

    case other =>
      log.warning("KafkaPublisher got some unknown message while producing: " + other)
  }

  def active(stream: SourceQueueWithComplete[KafkaMessage]): Receive = {
    case msg: T =>
      stream.offer(msg)

    case other =>
      log.warning("KafkaPublisher got the unknown message while producing: " + other)
  }
}
object KafkaPublisher {

  def props[T <: KafkaMessage: TypeTag] =
    Props(new KafkaPublisher[T])
}

I create an instance of the ProducerStreamActivated(...) in a parent Actor like this: 我在父Actor中创建ProducerStreamActivated(...)的实例,如下所示:

val stream = producerStream[DefaultMessage](producerProperties)
  def producerStream[T: Converter](producerProperties: Map[String, String]): SourceQueueWithComplete[T] = {
    if (Try(producerProperties("isEnabled").toBoolean).getOrElse(false)) {
      log.info(s"Kafka is enabled for topic ${producerProperties("publish-topic")}")
      val streamFlow = flowToKafka[T](producerProperties)
      val streamSink = sink(producerProperties)
      source[T].via(streamFlow).to(streamSink).run()
    } else {
      // We just Log to the Console and by pass all Kafka communication
      log.info(s"Kafka is disabled for topic ${producerProperties("publish-topic")}")
      source[T].via(flowToLog[T](log)).to(Sink.ignore).run()
    }
  }

When I now print the Type that is contained in the stream SourceQueueWithComplete[T] in my child actor, I get to see the base class KafkaMessage contained instead of the expected DefaultMessage. 现在,当我在子actor中打印流SourceQueueWithComplete [T]中包含的Type时,我会看到包含的基类KafkaMessage而不是预期的DefaultMessage。 Any ideas how to mitigate this? 有什么想法可以减轻这种情况吗?

In your KafkaPublisher 's receive method, you pattern match on a ProducerStreamActivated without any type parameters (you can't match on one with parameters because of type erasure), and in that method, the implicit TypeTag passed to paramInfo is decided at compile-time, at which point it is just a TypeTag[KafkaMessage] . 在您的KafkaPublisherreceive方法,你的模式匹配上ProducerStreamActivated没有任何类型的参数(你不能匹配一个有因为类型擦除参数),并在该方法中,隐含TypeTag传递给paramInfo在编译期决定时间,此时它只是TypeTag[KafkaMessage]

One way you should be able to work around this is to just have the ProducerStreamActivated class carry its own type tag with it, ie: 解决该问题的一种方法是,使ProducerStreamActivated类带有它自己的类型标签,即:

case class ProducerStreamActivated[T <: KafkaMessage](kafkaTopic: String, stream: SourceQueueWithComplete[T])(implicit val tag: TypeTag[T]) extends Event

Then rather than summoning one implicitly in the receive method, just do msg.tag . 然后执行msg.tag ,而不是在receive方法中隐式召唤一个。

This should work because at the point you actually create the ProducerStreamActivated , you do have the compile-time type parameter info (it's DefaultMessage ) and so that will be the type tag the compiler fills in, and you can then just keep a reference to that. 这应该行得通,因为在您实际创建ProducerStreamActivated那一刻,您确实具有编译时类型参数info(它是DefaultMessage ),因此它将是编译器填充的类型标记,然后您可以保留对它的引用。 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM