Сохранение аргументов типа в Akka receive

На этот вопрос как бы ответил Роланд Кун в этом post, однако, несмотря на несколько комментариев с просьбами о деталях, он не удосужился поделиться полным ответом.

Вот что я хочу сделать: у меня есть класс-оболочка case class Event[T](t: T), экземпляры которого я отправляю актеру Akka. Затем в методе receive этого актера я хочу различать Event[Int] и Event[String], что, очевидно, не так просто из-за стирания типов.

Роланд Кун делится в упомянутом посте тем, что «есть ровно один способ сделать это», то есть включение информации о типе в сообщение. Итак, я сделал это:

case class Event[T](t: T)(implicit val ct: ClassTag[T])

Несмотря на то, что разные люди просили предоставить его, Роланд Кун не говорит, что на самом деле делать в методе receive. Вот что я пробовал.

def receive = {
  case e: Event =>
    if (e.ct.runtimeClass == classOf[Int])
      println("Got an Event[Int]!")
    else if (e.ct.runtimeClass == classOf[String])
      println("Got an Event[String]!")
    else
      println("Got some other Event!")
  case _ =>
    println("Got no Event at all!")
}

Это лучшее, что я смог придумать, так как сложно разобраться в джунглях отражений Scala. Однако он не компилируется:

value ct is not a member of Any
else if (e.ct.runtimeClass == classOf[String])
           ^

Таким образом, я спрашиваю конкретно о том, как должен выглядеть метод receive.


person lkbaerenfaenger    schedule 13.11.2016    source источник
comment
Мне это кажется правильным (за исключением того, что проще сравнить ClassTag напрямую: e.ct == ClassTag.Int и e.ct == classTag[String]). В сообщении об ошибке у вас есть s.ct, которого нет в коде.   -  person Alexey Romanov    schedule 13.11.2016
comment
Изменил на e.ct. Я упростил приведенный выше код, чтобы он был автономным в этом посте. Однако сообщение об ошибке я скопировал из реального кода. Хороший улов, спасибо!   -  person lkbaerenfaenger    schedule 13.11.2016
comment
Вам также необходимо исправить case e: Event[_]. После этого компилируется: scastie.org/23724.   -  person Alexey Romanov    schedule 13.11.2016
comment
Опубликовать как ответ! Может быть, вы могли бы уточнить, как использовать сокращенную запись Event[T : ClassTag] в этом случае. Кроме того, какой будет String версия ClassTag.Int? Спасибо большое, Алексей!   -  person lkbaerenfaenger    schedule 13.11.2016
comment
Опубликовано. Вы не можете использовать T: ClassTag в этом случае (ну, вы можете, но в итоге получается более многословно). classTag[String] является String версией ClassTag.Int.   -  person Alexey Romanov    schedule 13.11.2016


Ответы (2)


После исправления ошибки Event takes type parameters:

def receive = {
  case e: Event[_] =>
    if (e.ct.runtimeClass == classOf[Int])
      println("Got an Event[Int]!")
    else if (e.ct.runtimeClass == classOf[String])
      println("Got an Event[String]!")
    else
      println("Got some other Event!")
  case _ =>
    println("Got no Event at all!")
}

код компилируется. Его можно немного упростить, не заглядывая внутрь ClassTags (разумеется, реализация ClassTag#equals будет сравнивать классы):

import scala.reflect.{ClassTag, classTag}

def receive = {
  case e: Event[_] =>
    if (e.ct == ClassTag.Int) // or classTag[Int]
      println("Got an Event[Int]!")
    else if (e.ct == classTag[String])
      println("Got an Event[String]!")
    else
      println("Got some other Event!")
  case _ =>
    println("Got no Event at all!")
}
person Alexey Romanov    schedule 13.11.2016

Вы также можете выполнить сопоставление с образцом для внутренней переменной внутри вложенного класса, и в этом случае он будет более кратким, вы можете использовать различные приемы сопоставления с образцом, и вам даже не нужен ClassTag: например

case class Event[T](t: T)    

def receive = {
  case Event(t: Int) => 
    println("Int")
  case Event((_: Float | _: Double)) => 
    println("Floating Point")
  case Event(_) =>
    println("Other")
}
person BryanM    schedule 25.11.2016