2015-06-29 11 views
13

Sto scrivendo una classe serializzabile che richiede diversi argomenti, tra cui un Function:Qual è il modo migliore per garantire che un argomento Function sia serializzabile?

public class Cls implements Serializable { 
    private final Collection<String> _coll; 
    private final Function<String, ?> _func; 

    public Cls(Collection<String> coll, Function<String, ?> func) { 
     _coll = coll; 
     _func = func;   
    } 
} 

func viene memorizzato in una variabile membro, e quindi ha bisogno di essere serializzabile. Java lambda are serializable if the type they're being assigned to is serializable. Qual è il modo migliore per garantire che il Function venga passato nel mio costruttore è serializzabile, se è stato creato utilizzando un lambda?

  1. creare un tipo SerializableFunction e l'uso che:

    public interface SerializableFunction<F, R> implements Function<F, R>, Serializable {} 
    .... 
    public Cls(Collection<String> coll, SerializableFunction<String, ?> func) {...} 
    

    Issues:

    • ora C'è una mancata corrispondenza tra gli argomenti coll e func, in quel func viene dichiarata come serializzabile nel firma, ma non lo è coll, ma entrambi devono essere serializzabili perché funzioni.
    • Non consente altre implementazioni di Function serializzabili.
  2. utilizzare un parametro di tipo sul costruttore:

    public <F extends Function<String, ?> & Serializable> 
    Cls(Collection<String> coll, F func) {...} 
    

    Issues:

    • Più flessibile di 1, ma più confusa.
    • C'è ancora una mancata corrispondenza tra i due argomenti - l'argomento func è tenuto ad attuare Serializable nel tipo gerarchia in fase di compilazione, ma coll è solo richiesto di essere serializzabile qualche modo (anche se questo requisito può essere gettato via, se necessario) .

    EDIT Questo codice realtà non compila quando si cerca di chiamare con un riferimento lambda o metodo.

  3. lasciarlo fino al chiamante

    Ciò richiede al chiamante di sapere (dai javadocs, o per tentativi ed errori) che l'argomento deve essere serializzabile, e gettato a seconda dei casi:

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)s -> ...); 
    

    o

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)Foo::processStr); 
    

    Questo è brutto IMO, e l'implementazione ingenua iniziale di utilizzare un lambda è garantito da rompere, piuttosto che funzionare come con coll (dato che la maggior parte delle collezioni è serializzabile in qualche modo). Questo spinge anche un implementazione dettaglio della classe sul chiamante.

Al momento sto appoggiato verso l'opzione 2, come quella che impone il minimo onere per il chiamante, ma io non credo che ci sia una soluzione ideale qui. Qualche altro suggerimento su come farlo correttamente?

MODIFICA: Forse è necessario uno sfondo. Questa è una classe che viene eseguita all'interno di storm, in un bullone, che viene serializzato per il trasferimento su un cluster rimosso da eseguire. La funzione sta eseguendo un'operazione sulle tuple elaborate durante l'esecuzione sul cluster. Quindi fa parte dello scopo della classe che è serializzabile e che l'argomento della funzione è serializzabile. Se non lo è, la classe non è affatto utilizzabile.

+0

Il problema con l'opzione 2 non sarebbe lo stesso se non esistesse un secondo parametro? – biziclop

+0

Non sono sicuro di come funzionerebbe se si riceve un lambda di cattura ... – assylias

+0

Ah sì, l'opzione 2 in realtà non viene compilata. L'ho rimosso. – thecoop

risposta

7

Nella maggior parte dei casi la risposta è: non.

Si può notare che la maggior parte delle classi di JRE, anche ObjectOutputStream.writeObject non applicano Serializable nella propria firma. Esistono semplicemente troppe API non specificamente alla serializzazione in cui le informazioni in fase di compilazione su un oggetto che implementa Serializable vengono perse e il loro utilizzo insieme alla serializzazione richiederebbe molto cast di tipi se quest'ultimo impone i propri input per essere Serializable.

Dal momento che uno dei tuoi parametri è un Collection, si può ottenere esempi da tale API:

Collections.unmodifiableList:

L'elenco restituito sarà serializzabile se la lista specificato è serializzabile.

Troverete più di tali operazioni, che cura per mantenere la capacità di serializzazione senza conservare il tipo Serializable fase di compilazione sul risultato.

Questo vale anche per tutti i tipi non public, ad es. i risultati di Collections.emptyList(), Arrays.asList(…) e Comparator.reverseOrder(). Sono tutti Serializable senza dichiararlo.


Inoltre, ogni classe avendo più casi d'uso di un semplice ottenere serializzato dovrebbe astenersi dal far rispettare per essere sempre Serializable. Ciò ostacolerebbe gli usi in cui non è coinvolta la serializzazione.

Per quanto riguarda il parametro Collection, è possibile prendere in considerazione la rimozione del vincolo serializzabile. Normalmente, proteggi la tua classe contro le modifiche successive alla raccolta che hai ricevuto. Una soluzione semplice è copiare la raccolta e quando la si fa, si può usare un tipo che supporta la serializzazione.

Anche se si vuole evitare la copia, la serializzazione di per sé un processo di copia per sé, in modo da poter semplicemente creare personalizzati readObject e writeObject metodi di deposito dei contenuti del Collection, eliminando la necessità di avere una collezione Serializable.


In sintesi, di solito la politica è che se l'utente della classe intende serializzare istanze di esso, è la responsabilità dell'utente che tutti i componenti messi in esso sono essi stessi Serializable.

+0

Aggiunte ulteriori informazioni sul motivo per cui è necessaria la serializzazione – thecoop

Problemi correlati