#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, но в то же время я потерял модульность. На данный момент это работает, но не является решением, которым я горжусь.