2013-06-25 11 views
6

Ich versuche, eine Nachricht mit Akka Remote-Aktoren senden, wobei die Fallklasse eine Unterklasse einer Superklasse ist, die Argument in seinem Konstruktor nimmt.Akka Remote-Aktoren, Superklasse ohne Standardkonstruktor

Hier ist ein Mindest-Beispiel, das Problem zu reproduzieren:

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://[email protected]: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") 
    } 
} 

Wenn dieser Code ausgeführt wird, erhalte ich folgende Fehlermeldung:

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

Ich denke, es liegt daran, dass die Nachricht nicht deserialisiert werden kann (Verwendung von akka.serialization.JavaSerializer), wegen des Konstruktors der Eltern. Wenn es nur ein oder zwei Nachrichten sind, weiß ich, dass ich meinen eigenen Serialisierer schreiben könnte, aber ich habe viele Fallklassen wie diese in meiner Anwendung.

Gibt es einen einfachen Weg, diese Art von Objekt mit entfernten Akteuren zu passieren?

Antwort

8

Dinge werden funktionieren, wenn Sie wie so umstrukturieren:

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

ich von Klassenvererbung mit Fallklassen im Allgemeinen versuchen und zu bleiben weg. Wenn ich in der Lage sein muss, auf einen Satz von Fallklassen als einen abstrakten Typ zu verweisen, verwende ich stattdessen Merkmale.

+1

Können Sie bitte erklären, was hier vor sich geht? Weder die MessageFoo noch die Ihre Frage hat einen No-Arg-Konstruktor. Warum arbeitet man mit Java-Serialisierung, wenn das andere nicht? –

+0

@DanielDarabos, scala Fallklassen sind serialisierbar. Wenn Sie sich den generierten Java-Code für eine Fallklasse ansehen, werden Sie sehen, dass er den Vertrag erfüllt, um serialisierbar zu sein. – cmbaxter

+0

Aber die Fallklasse in der Frage konnte nicht deserialisiert werden, oder? –

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

Wie cmbaxter Antwort weist darauf hin, dieses Muster, in dem die übergeordnete Klasse der Fall Klasse keinen Konstruktor ohne Argumente hat, führt zu InvalidClassException auf Deserialisierung. Per Cmbaxters Antwort ist die Vermeidung dieses Musters eine Lösung.

Aber was ist falsch in diesem Muster? Der Grund ist für Serializable in der API-Dokumentation dokumentiert:

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

Das Problem ist also, dass class A kein No-arg Konstruktor hat, zuzüglich es ist nicht Serializable. Also eine einfache Lösung ist es Serializable zu machen!

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

funktioniert wie ein Zauber, schneller Gewinn, um einfach ein 'extends' /' mit' hinzuzufügen, als um zu Eigenschaften zu restrukturieren. – Brett