Akka удаленные актеры, суперкласс без конструктора по умолчанию
Я пытаюсь отправить сообщение, используя удаленные актеры akka, где case-класс является подклассом суперкласса, принимающего аргумент в своем конструкторе.
Вот минимальный пример для воспроизведения проблемы:
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
), из-за родительского конструктора. Если бы это было только одно или два сообщения, я знал бы, что мог бы написать свой собственный сериализатор, но у меня есть много таких классов в моем приложении.
Будет ли какой-нибудь простой способ передать объект такого типа с использованием удаленных актеров?
2 ответа
Все будет работать, если вы реструктурируете так:
trait Foo{
val a:Int
}
case class MessageFoo(a:Int) extends Foo
Я обычно стараюсь держаться подальше от наследования классов с помощью case-классов. Если мне нужно иметь возможность ссылаться на набор классов case как абстрактный тип, я вместо этого использую черты.
class A(a: Int)
case class C() extends A(1)
Как указывает ответ cmbaxter, этот шаблон, в котором суперкласс класса case не имеет конструктора без аргументов, приводит к InvalidClassException
на десериализацию. Согласно ответу cmbaxter, избегание этого паттерна - одно из решений.
Но что не так в этом шаблоне? Причина описана в документации API для Serializable:
Чтобы разрешить сериализацию подтипов несериализуемых классов, подтип может взять на себя ответственность за сохранение и восстановление состояния открытых, защищенных и (если доступно) полей супертипа. Подтип может принять на себя эту ответственность, только если расширяемый класс имеет доступный конструктор без аргументов для инициализации состояния класса. Ошибочно объявлять класс Serializable, если это не так. Ошибка будет обнаружена во время выполнения.
Так что проблема в том, что class A
не имеет конструктора без аргументов, плюс его нетSerializable
, Таким образом, простое решение состоит в том, чтобы сделать это Serializable
!
class A(a: Int) extends Serializable
case class C() extends A(1)