Melhor tipo de segurança nas coleções Java
-
22-09-2019 - |
Pergunta
Na minha codificação Java, muitas vezes acabo com vários Map<String,Map<String,foo>>
ou Map<String,List<String>>
E então tenho problemas para lembrar qual string é qual chave. Eu comento a declaração com //Map<capabiltyId,Map<groupId,foo>>
ou //Map<groupId,List<capabilityId>
, mas não é a melhor solução. Se String não fosse final, eu faria novas aulas CapabilityId extends String
e GroupId extends String
, mas eu não posso. Existe uma maneira melhor de acompanhar qual coisa é a chave e talvez fazer com que o compilador a aplique?
Solução
Em vez de ter CapabilityId
ampliar String
, CapabilityId
poderia incluir um String
campo chamado "id"; então você Map
poderia ser definido como Map<CapabilityId, Map<GroupId, Foo>>
, e você pode chegar aos campos de identificação individuais através de um getId()
nas suas principais classes.
Não tenho certeza se faria isso sozinho, mas se o fizesse, é provavelmente isso que eu faria.
Você pode limitar a desordem por ter um abstract GenericId
classe com um campo de identificação e getId()
método, e tem CapabilityId
e GroupId
herdar dele.
Outras dicas
Enrole as cordas em classes de invólucro, se você quiser:
class GroupId implements Comparable {
private String groupId;
public GroupId (String groupId) {
this.groupId = groupId;
}
...
}
Map<GroupId, List<CapabilityId>> m = ...
Criar um ID
classe que você pode subclasse e que consiste em um String
campo e implementações de equals()
e hashCode()
que usam esse campo.
Eu colocaria tudo em classe única e utilizava nomes sensatos de campo/método/argumento.
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..
}
Dessa forma, você pode ver no método/argumento nomes o que ele espera/retorna.
Existem várias maneiras de seguir este (algumas já mencionadas):
- Como @Roman, embrulhe o tipo de propósito geral em um tipo mais específico, que fornece uma digitação mais forte. Digitação forte boa, IMO.
- Como @Nanda, use um tipo de coleção mais específico. A biblioteca Java é um pouco pobre nesta área. Depende de como você se sente sobre as dependências.
- Como @balusc, mova todas as coisas nojentas para uma aula nojenta. Realmente não remove o problema, mas ele o contém (como em Ghostbusters).
Map<String,Map<String,foo>>
Parece que você tem uma chave composta, ou seja, uma chave que compreende duas partes. Portanto, introduza uma classe de chave composta imutável, que é um objeto de valor que representa os dois objetos de valor de componentes.
Ao invés de Map<String,List<String>>
Você deve usar o MultiMAP do Google Guava / Google Collection
Adicionando às outras respostas:
Enrole -o.
Não é apenas uma solução para o seu problema, mas uma boa ideia em geral, ou seja, evite parâmetros simples. Seu código ganhará legibilidade, sanidade e manutenção. Você pode adicionar todos os tipos de propriedades agradáveis, por exemplo, declarar @imutable. Como você descobriu, é melhor lembrar e controlar. Você possui a classe e pode fazer o que quiser com ela.