Исключение ClassCastException при создании субъекта для понимания

#scala #akka #scala-option #self-type

#scala #akka #scala-опция #самотипирование

Вопрос:

У меня есть актер, который создает дочерних актеров типа Child1 в этом примере. Child1 конструктор принимает две строки, которые извлекаются из переменных, которые находятся в SomeTrait том, что смешивается с SomeActor isntance.

 trait SuperTrait {
  lazy val str1: Option[String] = None
  lazy val str2: Option[String] = None
} 
trait SomeTrait extends SuperTrait {
  override lazy val str1: Option[String] = Some("Str1")
  override lazy val str2: Option[String] = Some("Str2")
}
class SomeActor extends Actor {
  this: SuperTrait => 
  var child: Option[ActorRef] = None
  override def preStart(): Unit = {
    child = for {
      st1 <- str1 
      st2 <- str2
    } yield context.actorOf(Child1.props(st1, st2)))
  }
}
  

Создание в SomeActor экземпляре:

 context.actorOf(Props[SomeActor with SomeTrait])
  

При этом я получаю странную ошибку:

SomeActor cannot be cast to SomeTrait .

Кажется, что извлечение переменных из контейнера опций SomeTrait вызывает это исключение.

Чего мне здесь не хватает?

И это происходит не только с for comprehension s. Также, когда я пытаюсь сделать str1.getOrElse("") или добавить геттер в SomeTrait : def getStr1 = str1.getOrElse("")

Комментарии:

1. Как вы создаете экземпляры SomeActor ? new SomeActor with SomeTrait ? Почему бы просто не class SomeActor extends Actor with SomeTrait оставить тип self?

2. Хорошо, это работает, когда я расширяю признак, а не добавляю его. Вы знаете, почему? Я решил добавить его по соображениям полезности, которые сейчас не важны.

3. Я мало что знаю об Akka, иначе я бы опубликовал ответ, но я бы предположил, что Akka создает экземпляры SomeActor посредством отражения или каким-либо другим способом, который не позволяет scalac обеспечить соответствие экземпляров типам self. Вы могли бы сделать SomeActor абстрактным, чтобы убедиться, что это так.

4. Я обнаружил еще одну проблему. Допустим SomeTrait , это признак, из SupSomeTrait которого во время выполнения str1 берутся значения и str2 SupSomeTrait . Это очень неудобно.

5. @goral: это связано с порядком инициализации признаков. Если вам нужен нормальный порядок инициализации, всегда используйте lazy val или def в чертах, никогда val .

Ответ №1:

Как сказал @ggovan выше, при использовании Akka вы не можете контролировать построение актера. Библиотека Akka позаботится об этом (и именно поэтому у вас есть реквизиты для инкапсуляции параметров, переданных конструктору). Причина этого в том, что если ваш актер выходит из строя, его супервизор должен иметь возможность создать его новый экземпляр.

Когда вы используете Props[X], Scala использует a ClassTag для поиска класса среды выполнения субъекта. Однако, ClassTag похоже, что он не распознает анонимный класс, созданный при вызове Props[X with Y] , он распознает только базовый класс (в вашем случае, SomeActor ). Способ обойти это — использовать альтернативу реквизита по имени, чтобы вы могли сделать это при создании SomeActor:

 val actor = sys.actorOf(Props(new SomeActor with SomeTrait))
  

Это работает, и он также забирает переопределенные lazy val. Однако обязательно ознакомьтесь с недостатками этого в http://doc.akka.io/docs/akka/snapshot/scala/actors.html (раздел «Опасные варианты»).

Другим вариантом было бы использовать шаблон Cake:

 trait SomeTrait {
  lazy val str1: Option[String] = None
  lazy val str2: Option[String] = None
}

trait ActorTrait extends Actor {
  this: SomeTrait => 
  override lazy val str1 = Some("Str1")
  override lazy val str2 = Some("Str2")
  var child: Option[ActorRef] = None
  override def preStart(): Unit = {
    child = for {
      st1 <- str1 
      st2 <- str2
    } yield context.actorOf(Child1.props(st1, st2)))
  }
}

class SomeActor extends ActorTrait with SomeTrait
  

В этом случае вы можете использовать рекомендуемый Props[SomeActor] метод для создания объекта Props.

Комментарии:

1. Супервизор актера создает его, расширяя надлежащие черты. Вот так context.actorOf(Props[SomeActor with SomeTrait]) . Он по-прежнему принимает значения из super trait . Я отредактировал вопрос, чтобы добавить это.

2. Я использовал сценарий для создания актера, показанный в документации akka, но это все равно не помогает. Я каким-то образом нашел обходной путь, объединив это и предложенный вами шаблон Cake, но в то же время я потерял модульность. На данный момент это работает, но не является решением, которым я горжусь.