2013-06-25 9 views
6

Sto tentando di inviare un messaggio usando gli attori remoti akka, dove la classe case è una sottoclasse di una superclasse che prende argomento nel suo costruttore.Attori remoti Akka, superclasse senza costruttore predefinito

Ecco un esempio minima per riprodurre il problema:

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") 
    } 
} 

Quando si esegue questo codice, ottengo il seguente errore:

[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] 

Penso che sia perché il messaggio non può essere deserializzato (usando akka.serialization.JavaSerializer), a causa del costruttore dei genitori. Se fosse solo uno o due messaggi so che potrei scrivere il mio serializzatore, ma ho molte classi di casi come questo nella mia applicazione.

Esiste un modo semplice per passare questo tipo di oggetto utilizzando gli attori remoti?

risposta

8

cose funzionano se si ristruttura in questo modo:

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

Io in genere cerco di stare lontano dalla classe eredità con classi case. Se ho bisogno di essere in grado di riferirsi a un insieme di case classes come un tipo astratto, uso invece i tratti.

+1

Potete spiegare cosa sta succedendo qui? Né il 'MessageFoo' della domanda né il tuo hanno un costruttore no-arg. Perché si lavora con la serializzazione Java quando l'altra no? –

+0

@DanielDarabos, le classi di caso scala sono serializzabili. Se si guarda il codice java generato per una classe di case, si vedrà che soddisfa il contratto per essere serializzabile. – cmbaxter

+0

Ma la classe del caso nella domanda non può essere deserializzata, giusto? –

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

Come risposta di cmbaxter sottolinea, questo modello, in cui la superclasse della classe caso non ha un costruttore no-arg, porta a InvalidClassException sulla deserializzazione. Per ogni risposta di cmbaxter, evitare questo modello è una soluzione.

Ma cosa c'è di sbagliato in questo schema? Il motivo è documentato nella documentazione di API per Serializable:

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.

Quindi il problema è che class A non dispone di un costruttore no-arg, più non è Serializable. Quindi una soluzione semplice è quella di renderlo Serializable!

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

funziona come un incantesimo, una rapida vittoria per aggiungere semplicemente un 'extends'/'with' come indicato nella ristrutturazione dei tratti. – Brett