Scala 在中创建参与者以供理解时的ClassCastException

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

在本例中,我有一个actor创建类型为
Child1
的子actor
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不能强制转换为SomeTrait

似乎从
SomeTrait
的选项容器中提取变量会引发该异常

我错过了什么


这不仅仅发生在理解方面。同样,当我尝试执行str1.getOrElse(“”或向SomeTrait添加getter时:
def getStr1=str1.getOrElse(“”
),正如@ggovan上面所说,当使用Akka时,你无法控制参与者的构造。Akka库负责这一点(这就是为什么您有一些道具来封装传递给构造函数的参数)。原因是,如果您的参与者崩溃,其主管需要能够创建一个新的实例

当您使用Props[X]时,Scala使用a
ClassTag
来查找参与者的运行时类。但是,
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