【问题标题】:Akka remote actors, superclass without default constructorAkka 远程演员,没有默认构造函数的超类
【发布时间】:2013-06-22 16:19:30
【问题描述】:

我正在尝试使用 akka 远程参与者发送消息,其中案例类是超类的子类,在其构造函数中接受参数。

这是重现问题的最小示例:

package com.tuvistavie.testremote

import akka.actor.{ Actor, ActorSystem, Props, ActorLogging }
import com.typesafe.config.ConfigFactory

abstract class Foo(val a: Int)
case class MessageFoo(override val a: Int) extends Foo(a)

object Sender {
  def main(args: Array[String]) {
    val system = ActorSystem("Sender", ConfigFactory.load.getConfig("sender"))
    val actor = system.actorFor("akka://Receiver@127.0.0.1:2552/user/receiver")
    actor ! MessageFoo(1)
  }
}

object Receiver {
  class ReceiverActor extends Actor with ActorLogging {
    def receive = {
      case m: MessageFoo => log.debug(m.toString)
    }
  }

  def main(args: Array[String]) {
    val system = ActorSystem("Receiver", ConfigFactory.load.getConfig("receiver"))
    val actor = system.actorOf(Props[ReceiverActor], "receiver")
  }
}

运行此代码时,我收到以下错误:

[ERROR] [06/26/2013 02:53:16.132] [Receiver-9] 
[NettyRemoteTransport(akka://Receiver@127.0.0.1:2552)] 
RemoteServerError@akka://Receiver@127.0.0.1:2552] Error[java.io.InvalidClassException: com.tuvistavie.testremote.MessageFoo; no valid constructor]

我认为这是因为父母的构造函数无法反序列化消息(使用akka.serialization.JavaSerializer)。 如果只有一两条消息,我知道我可以编写自己的序列化程序,但我的应用程序中有很多这样的案例类。

是否有任何简单的方法可以使用远程参与者传递这种对象?

【问题讨论】:

    标签: scala akka actor remote-actors


    【解决方案1】:
    class A(a: Int)
    case class C() extends A(1)
    

    就像 cmbaxter 的回答指出的那样,这种模式,其中案例类的超类没有无参数构造函数,导致 InvalidClassException 反序列化。根据 cmbaxter 的回答,避免这种模式是一种解决方案。

    但是这种模式有什么问题呢?原因记录在Serializable 的 API 文档中:

    为了允许序列化不可序列化类的子类型,子类型可能会负责保存和恢复超类型的公共、受保护和(如果可访问)包字段的状态。只有当它扩展的类具有可访问的无参数构造函数来初始化类的状态时,子类型才可以承担此责任。如果不是这种情况,则声明类 Serializable 是错误的。将在运行时检测到错误。

    所以问题是class A 没有无参数构造函数,加上它不是Serializable。所以一个简单的解决办法就是设为Serializable!

    class A(a: Int) extends Serializable
    case class C() extends A(1)
    

    【讨论】:

    • 工作就像一个魅力,快速获胜,只需添加一个extends / with 与重组特征相反。
    【解决方案2】:

    如果你像这样重组,一切都会好起来的:

    trait Foo{
      val a:Int
    }
    case class MessageFoo(a:Int) extends Foo
    

    我通常会尽量远离案例类的类继承。如果我需要能够将一组案例类引用为抽象类型,我会改用特征。

    【讨论】:

    • 你能解释一下这里发生了什么吗?问题的MessageFoo 和您的问题都没有无参数构造函数。为什么一个使用 Java 序列化,而另一个不使用?
    • @DanielDarabos,scala 案例类是可序列化的。如果您查看为案例类生成的 java 代码,您会发现它符合可序列化的约定。
    • 但是问题中的case类不能反序列化吧?
    • 谢谢!你的建议有效。看起来规则是案例类的超类构造函数必须是无参数的。我只是觉得这是一个令人费解的限制。但是我没有勇气去看生成的Java代码:)。
    • 看起来这实际上不是 Scala 问题!我只是对Java序列化一无所知。我添加了一个替代解决方案的答案。当您的方法无法应用时,它可能会很有用。
    猜你喜欢
    • 2012-09-01
    • 2018-07-01
    • 2022-01-01
    • 2023-03-20
    • 2016-07-18
    • 1970-01-01
    • 1970-01-01
    • 2014-04-15
    • 2015-07-07
    相关资源
    最近更新 更多