Meglio sicurezza di tipo a collezioni di Java
-
22-09-2019 - |
Domanda
Nel mio codice Java, che spesso finiscono con diversi Map<String,Map<String,foo>>
o Map<String,List<String>>
e poi devo ricordare che problemi String è quale tasto. Io commento la dichiarazione con //Map<capabiltyId,Map<groupId,foo>>
o //Map<groupId,List<capabilityId>
, ma non è la soluzione ottimale. Se stringa non era definitivo, vorrei fare nuove classi CapabilityId extends String
e GroupId extends String
, ma non ci riesco. C'è un modo migliore per tenere traccia di quali cosa è la chiave e magari avere il compilatore farla rispettare?
Soluzione
Invece di avere CapabilityId
estendere String
, CapabilityId
potrebbe includere un campo String
chiamato "id"; allora il vostro Map
potrebbe essere definito come Map<CapabilityId, Map<GroupId, Foo>>
, e si potrebbe arrivare a singoli campi ID attraverso una getId()
sulle classi principali.
Non sono sicuro che avrei fatto io stesso, ma se ho fatto, questo è probabilmente quello che avrei fatto.
Si potrebbe limitare l'ingombro di avere una classe abstract GenericId
con un metodo campo id e getId()
, e hanno CapabilityId
e GroupId
ereditare da esso.
Altri suggerimenti
stringhe Wrap in wrapper-classi, se si desidera:
class GroupId implements Comparable {
private String groupId;
public GroupId (String groupId) {
this.groupId = groupId;
}
...
}
Map<GroupId, List<CapabilityId>> m = ...
Creare una classe ID
cui è possibile creare una sottoclasse, e che consiste in un campo String
e implementazioni di equals()
e hashCode()
che utilizzano quel campo.
Vorrei mettere tutto in un'unica classe e fare uso di nomi di campo / metodo / argomenti sensibili.
public class GroupCapabilities {
private Map<String, Map<String, Group>> groupCapabilities;
public void addGroup(String capabilityId, Group group) {
Map<String, Group> groups = groupCapabilities.get(capabilityId);
if (groups = null) {
groups = new HashMap<String, Group>();
groupCapabilities.put(capabilityId, group);
}
groups.put(group.getId(), group);
}
public Map<String, Group> getGroups(String capabilityId) {
return groupCapabilities.get(capabilityId);
}
public Group getGroup(String capabilityId, String groupId) {
Map<String, Group> groups = groupCapabilities.get(capabilityId);
return (groups != null) ? groups.get(groupId) : null;
}
// Etc..
}
In questo modo la si può vedere a nomi di metodo / argomenti quello che si aspetta / rendimento.
Ci sono un certo numero di modi per andare su questo (alcuni già citati):
- Come @Roman, avvolgere il tipo general purpose in un tipo più specifico, che dà forte tipizzazione. Tipizzazione forte buono, IMO.
- Come @nanda, utilizzare un tipo di raccolta più specifica. La libreria Java è un po 'scarso in questo settore. Dipende di come ci si sente su dipendenze.
- Come @BalusC, spostare tutta la roba icky in una classe icky. in realtà non eliminare il problema, ma non lo contengono (come in Ghostbusters).
-
Map<String,Map<String,foo>>
assomiglia molto si dispone di una chiave composta, vale a dire una chiave che si compone di due parti. Quindi, introdurre una classe chiave composita immutabile, che è un oggetto valore che rappresenta i due oggetti di valore dei componenti.
Invece di Map<String,List<String>>
si dovrebbe usare Multimap da Google Guava / Google Collection
L'aggiunta alle altre risposte:
Avvolgerlo.
Non è solo una soluzione al tuo problema, ma una buona idea, in generale, vale a dire evitare semplice parametri. Il tuo codice guadagnerà la leggibilità, la sanità mentale e la manutenibilità. È possibile aggiungere tutti i tipi di belle proprietà ad esso, ad esempio, dichiararla @Immutable. Come hai scoperto in questo modo è meglio ricordare e da controllare. Già la classe e può fare quello che vuoi con esso.