7

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親のコンストラクターが原因で、メッセージを(を使用して)逆シリアル化できないためだと思います。1 つまたは 2 つのメッセージだけであれば、独自のシリアライザーを作成できることはわかっていますが、アプリケーションにはこのようなケース クラスがたくさんあります。

リモート アクターを使用してこの種のオブジェクトを渡す簡単な方法はありますか?

4

2 に答える 2

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

cmbaxter の回答が指摘しているように、ケース クラスのスーパークラスに引数なしのコンストラクタがないこのパターンは、InvalidClassException逆シリアル化につながります。cmbaxter の回答によると、このパターンを回避することが 1 つの解決策です。

しかし、このパターンのどこが悪いのでしょうか? その理由は、 Serializableの API ドキュメントに記載されています。

シリアル化できないクラスのサブタイプをシリアル化できるようにするために、サブタイプは、スーパータイプの public フィールド、protected フィールド、および (アクセス可能な場合) package フィールドの状態を保存および復元する責任を負う場合があります。サブタイプは、それが拡張するクラスに、クラスの状態を初期化するためのアクセス可能な引数なしのコンストラクターがある場合にのみ、この責任を負うことができます。そうでない場合、Serializable クラスを宣言するとエラーになります。エラーは実行時に検出されます。

したがって、問題は、class A引数のないコンストラクターがないことと、ないことですSerializable。だから簡単な解決策はそれを作ることSerializableです!

class A(a: Int) extends Serializable
case class C() extends A(1)
于 2015-01-28T10:54:24.517 に答える
9

次のように再構築すると、うまくいきます。

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

私は通常、ケース クラスを使用したクラス継承を避けるようにしています。ケースクラスのセットを抽象型として参照できるようにする必要がある場合は、代わりに特性を使用します。

于 2013-06-25T19:16:22.583 に答える