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.
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