Повышенная безопасность типов в коллекциях Java
-
22-09-2019 - |
Вопрос
В моем java-кодировании я часто получаю несколько Map<String,Map<String,foo>>
или Map<String,List<String>>
и тогда мне становится трудно вспомнить, какая строка является ключом.Я комментирую это заявление следующим образом //Map<capabiltyId,Map<groupId,foo>>
или //Map<groupId,List<capabilityId>
, но это не самое лучшее решение.Если бы String не был окончательным, я бы создал новые классы CapabilityId extends String
и GroupId extends String
, но я не могу.Есть ли лучший способ отслеживать, какая вещь является ключом, и, возможно, заставить компилятор применить ее?
Решение
Вместо того, чтобы иметь CapabilityId
продлевать String
, CapabilityId
мог бы включать String
поле под названием «id»;тогда твой Map
можно определить как Map<CapabilityId, Map<GroupId, Foo>>
, и вы можете получить доступ к отдельным полям идентификатора через getId()
на ваших ключевых занятиях.
Я не уверен, что сделал бы это сам, но если бы я это сделал, вероятно, я бы сделал именно это.
Вы можете ограничить беспорядок, установив abstract GenericId
класс с полем id и getId()
метод и иметь CapabilityId
и GroupId
наследовать от него.
Другие советы
Оберните строки в классы-оболочки, если хотите:
class GroupId implements Comparable {
private String groupId;
public GroupId (String groupId) {
this.groupId = groupId;
}
...
}
Map<GroupId, List<CapabilityId>> m = ...
Создать ID
класс, который вы можете подклассифицировать и который состоит из String
области и реализации equals()
и hashCode()
которые используют это поле.
Я бы поместил все это в один класс и использовал разумные имена полей/методов/аргументов.
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..
}
Таким образом, вы можете увидеть в именах методов/аргументов то, что он ожидает/возвращает.
Есть несколько способов сделать это (некоторые уже упоминались):
- Как и @Roman, оберните тип общего назначения в более конкретный тип, который обеспечивает более строгую типизацию.Строгая типизация хороша, ИМХО.
- В качестве @nanda используйте более конкретный тип коллекции.Библиотека Java в этой области немного скудна.Это зависит от того, как вы относитесь к зависимостям.
- В роли @BalusC переместите все неприятные вещи в неприятный класс.На самом деле проблему не устраняет, но сдерживает ее (как в «Охотниках за привидениями»).
Map<String,Map<String,foo>>
очень похоже, что у вас составной ключ, т.е.ключ, состоящий из двух частей.Итак, представьте неизменяемый класс составного ключа, который представляет собой объект значения, представляющий два объекта значения компонента.
Вместо Map<String,List<String>>
вам следует использовать Multimap из Google Guava/Google Collection
Добавление к другим ответам:
Заверните это.
Это не просто решение вашей проблемы, но и хорошая идея в целом, т.е.избегайте простых параметров.Ваш код станет удобочитаемым, разумным и ремонтопригодным.Вы можете добавить к нему всевозможные приятные свойства, напримеробъявите его @Неизменяемым.Как вы выяснили, этот способ лучше запоминать и контролировать.Вы владеете классом и можете делать с ним все, что вам заблагорассудится.