2015-04-26 12 views
5

Abbiamo creato una classe astratta che usiamo per trattare con Redis (set/ottenere i valori), che si presenta come segue:generici Java e Serializable

public abstract class AbstractCachedSupport<T extends Serializable> { 
    protected T get(CacheKey key, Supplier<T> supplier) {...} 
    // ... 
} 

Quello che non sono felice è che non possiamo usare interfacce come List, Map quando si estende questa classe:

public class CachedMap extends AbstractCachedSupport<Map<String, Integer>> 

perché non estendere Serializable, quindi dobbiamo usare sempre classi concrete:

public class CachedMap extends AbstractCachedSupport<HashMap<String, Integer>> 

Inutile dire che questo ha la sua parte di problemi durante la migrazione, ad esempio, da una classe concreta all'altra. Non è nemmeno qualcosa che definirei best practice, ma forse sono solo io.

Un'alternativa che ci dà la flessibilità di lavorare con le interfacce sarebbe rimuovere la tipizzazione limitata e controllare a runtime se T estende Serializable:

public abstract class AbstractCachedSupport<T> { 
    public AbstractCachedSupport() { 
     final Class<T> type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; 
     if (!Serializable.class.isAssignableFrom(type)) { 
      throw new RuntimeException("T must extend Serializable"); 
     } 
    } 

    protected T get(CacheKey key, Supplier<T> supplier) {...} 
    // ... 
} 

Questo ci lascia senza fase di compilazione controllando T estende Serializable, neanche una cosa carina.

Conoscete in qualche modo come potremmo risolvere questo elegantemente? Preferiresti usare il primo (parametro di tipo limitato) o il secondo (solo il controllo di runtime)?

Un compromesso potrebbe essere quella di andare per la prima e utilizzare sempre una classe contenitore per lo svolgimento della collezione:

public class IntegerParamsContainer implements Serializable { 
    private static final long serialVersionUID = 1L; 
    private Map<String, Integer> map; 
} 

public class CachedMap extends AbstractCachedSupport<IntegerParamsContainer> 

Ma questo non ha, alla fine, lo stesso problema come il secondo: non c'è tempo di compilazione controllo , la responsabilità spetta agli sviluppatori di utilizzare sempre le raccolte che implementano Serializable.

Edit: Alcune delle classi estendono AbstractCachedSupport sono la primavera (versione 4.1 attualmente) classi di componenti e non sono chiamati CachedMap o qualcosa di simile, piuttosto CityAutocompleteDataBean, WrParamsDataBean ecc .. Se aggiungiamo generici a queste classi di componenti , finiremo con dichiarazioni come:

@Inject 
private CityAutocompleteDataBean<ArrayList<String>>; 
@Inject 
private WrParamsDataBean<HashMap<String, WrData>>; 

al contrario di

@Inject 
private CityAutocompleteDataBean; 
@Inject 
private WrParamsDataBean; 

Il motivo dell'utilizzo di <ArrayList<String>> e <HashMap<String, WrData>> sfuggirà alla maggior parte degli sviluppatori quando vedranno tali righe di codice. Trovo anche piuttosto brutto, considerando dove abbiamo iniziato e per cosa lo usiamo.

Tuttavia, questo funziona come richiesto da me, grazie Jesper.

+0

Come nota a margine, non credo che i lavori di controllo in fase di esecuzione, vale a dire se si dispone di un 'CachedMap classe estende AbstractCachedSupport > 'si ha lo stesso problema in cui l'argomento' T' non è serializzabile. Ciò che è effettivamente necessario convalidare sono i valori di campo di ogni istanza, non le dichiarazioni. (Ma dovrebbe essere che la serializzazione lo faccia già.) – Radiodef

risposta

5

È possibile farlo utilizzando la seguente sintassi:

public abstract class AbstractCachedSupport<T extends Serializable> { 
    // ... 
} 

public class CachedMap<T extends Map<String, Integer> & Serializable> 
     extends AbstractCachedSupport<T> { 
    // ... 
} 

Ciò significa che il tipo T deve implementare Map<String, Integer> e anche Serializable.

allora si potrebbe utilizzare CachedMap con uno specifico Map implementazione che implementa Serializable:

CachedMap<HashMap<String, Integer>> cachedMap = new CachedMap<>(); 
+0

Mi hai battuto, stavo per postarlo :) – Joffrey

+0

Ciò richiederebbe Java 8? – CKing

+1

@bot No, questo non richiede Java 8. – Jesper