Scala 在中创建参与者以供理解时的ClassCastException
在本例中,我有一个actor创建类型为Scala 在中创建参与者以供理解时的ClassCastException,scala,akka,scala-option,self-type,Scala,Akka,Scala Option,Self Type,在本例中,我有一个actor创建类型为Child1的子actorChild1构造函数获取两个字符串,这两个字符串是从SomeTrait中的变量中提取的,该变量被混合到SomeActorisntance中 trait SuperTrait { lazy val str1: Option[String] = None lazy val str2: Option[String] = None } trait SomeTrait extends SuperTrait { override
Child1
的子actorChild1
构造函数获取两个字符串,这两个字符串是从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不能强制转换为SomeTrait
似乎从SomeTrait
的选项容器中提取变量会引发该异常
我错过了什么
这不仅仅发生在理解方面。同样,当我尝试执行str1.getOrElse(“”或向SomeTrait添加getter时:
def getStr1=str1.getOrElse(“”
),正如@ggovan上面所说,当使用Akka时,你无法控制参与者的构造。Akka库负责这一点(这就是为什么您有一些道具来封装传递给构造函数的参数)。原因是,如果您的参与者崩溃,其主管需要能够创建一个新的实例
当您使用Props[X]时,Scala使用aClassTag
来查找参与者的运行时类。但是,ClassTag
似乎没有拾取调用Props[X with Y]
时创建的匿名类,它只拾取基类(在您的示例中,SomeActor
)。解决这一问题的一种方法是使用道具的“按名称”选项,因此在创建SomeActor时可以这样做:
val actor = sys.actorOf(Props(new SomeActor with SomeTrait))
这是可行的,它也会接收被覆盖的惰性VAL。但是,请务必阅读(第“危险变体”部分)中的缺点
另一种选择是使用蛋糕图案:
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
trait sometracit{
lazy val str1:选项[字符串]=无
lazy val str2:选项[字符串]=无
}
trait ActorTrait扩展了Actor{
this:SomeTrait=>
覆盖延迟值str1=Some(“str1”)
覆盖延迟值str2=Some(“str2”)
变量子项:选项[ActorRef]=无
覆盖def preStart():单位={
孩子{
st1你是如何创建SomeActor
的实例的?新的SomeActor和SomeTrait
?为什么不干脆类SomeActor用SomeTrait扩展Actor和退出self类型呢?好吧,当我扩展了这个特性而不是将它混合在一起时,它就起作用了。你知道为什么吗?我选择将它混合在一起是因为现在不重要的有用的原因。我我对Akka了解不多,否则我会发布一个答案,但我会假设Akka是通过反射或其他方式创建某个参与者的实例,不允许scalac
强制执行实例符合自身类型。你可以将某个参与者抽象化,看看是否是这样。我发现了另一个问题。比方说SomeTrait
是SupSomeTrait
的一个子集,然后在运行时,str1
和str2
的值取自SupSomeTrait
。这非常不方便。@goral:这与属性初始化的顺序有关。如果您想要一个合理的初始化顺序,请始终使用lazy val
或def
在traits中,从不val
。参与者的主管创建它,扩展适当的traits。像这样context.actorOf(Props[SomeActor with SomeTrait])
。它仍然从super-trait中获取价值。我编辑了问题以添加这一点。我一直在使用场景来创建akka文档中显示的演员,但它仍然没有帮助。我不知何故将此与您提出的蛋糕模式相结合,提出了一个解决方案,但同时我失去了模块性。目前它正在工作,但不是解决方案我感到自豪的是我的爱。
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