Лучше ли возвращать ImmutableMap или карту?
Скажем, я пишу метод, который должен возвращать Map. Например:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Подумав об этом некоторое время, я решил, что нет причины модифицировать эту карту после ее создания. Таким образом, я хотел бы вернуть ImmutableMap.
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Должен ли я оставить возвращаемый тип как общую карту, или я должен указать, что возвращаю ImmutableMap?
С одной стороны, именно поэтому были созданы интерфейсы; чтобы скрыть детали реализации.
С другой стороны, если я оставлю это так, другие разработчики могут упустить тот факт, что этот объект неизменен. Таким образом, я не стану достигать главной цели неизменяемых объектов; чтобы сделать код более понятным, минимизируя количество объектов, которые могут измениться. Даже хуже, через некоторое время кто-то может попытаться изменить этот объект, и это приведет к ошибке времени выполнения (компилятор не будет предупреждать об этом).
Ответы
Ответ 1
-
Если вы пишете API с открытым доступом и что неизменность является важным аспектом вашего дизайна, я определенно сделаю его явным, если бы имя метода явно означало, что возвращаемая карта будет неизменной или возвращая конкретный тип карты. Упоминать его в javadoc недостаточно, на мой взгляд.
Поскольку вы, по-видимому, используете реализацию Guava, я смотрел на документ, и это абстрактный класс, поэтому он дает вам немного гибкости в конкретном конкретном типе.
-
Если вы пишете внутренний инструмент/библиотеку, становится гораздо более приемлемым просто вернуть простой Map
. Люди будут знать о внутренностях кода, который они звонят, или, по крайней мере, будут иметь легкий доступ к нему.
Мое заключение было бы в том, что явное - это хорошо, не оставляйте вещи на произвол.
Ответ 2
В качестве возвращаемого типа вы должны иметь ImmutableMap
. Map
содержит методы, которые не поддерживаются реализацией ImmutableMap
(например, put
) и помечены @deprecated
в ImmutableMap
.
Использование устаревших методов приведет к предупреждению компилятора, и большинство IDE будут предупреждать, когда люди пытаются использовать устаревшие методы.
Это предварительное предупреждение предпочтительнее наличия исключений во время выполнения в качестве вашего первого намека на то, что что-то не так.
Ответ 3
С другой стороны, если я оставлю это так, другие разработчики могут упустить тот факт, что этот объект является неизменным.
Вы должны упомянуть об этом в javadocs. Вы знаете, разработчики читают их.
Таким образом, я не стану достигать главной цели неизменяемых объектов; сделать кода более четко, минимизируя количество объектов, которые могут измениться. Даже самое худшее, через какое-то время кто-то может попытаться изменить этот объект, и это приведет к ошибке выполнения (компилятор не будет предупреждать об этом).
Ни один разработчик не публикует свой код непроверенным. И когда он проверяет это, он получает исключение, когда он видит не только причину, но и файл и строку, где он пытался записать неизменную карту.
Обратите внимание, что только сам Map
будет неизменным, а не те объекты, которые он содержит.
Ответ 4
если я оставлю это так, другие разработчики могут упустить тот факт, что этот объект является неизменным
Это правда, но другие разработчики должны проверить свой код и убедиться, что он покрыт.
Тем не менее у вас есть еще два варианта решения этой проблемы:
-
Использовать Javadoc
@return a immutable map
-
Выберите имя описательного метода
public Map<String, Integer> getImmutableMap()
public Map<String, Integer> getUnmodifiableEntries()
Для конкретного варианта использования вы можете даже лучше назвать методы. Например.
public Map<String, Integer> getUnmodifiableCountByWords()
Что еще вы можете сделать?!
Вы можете вернуть
-
копия
private Map<String, Integer> myMap;
public Map<String, Integer> foo() {
return new HashMap<String, Integer>(myMap);
}
Этот подход следует использовать, если вы ожидаете, что многие клиенты изменят карту, и пока карта содержит только несколько записей.
-
CopyOnWriteMap
копия на коллекциях записи обычно используется, когда вам нужно иметь дело с
concurrency. Но эта концепция также поможет вам в вашей ситуации, поскольку
CopyOnWriteMap создает копию внутренней структуры данных в мутативной операции (например, добавляет, удаляет).
В этом случае вам нужна тонкая оболочка вокруг вашей карты, которая делегирует все вызовы методов на базовую карту, за исключением мутационных операций. Если вы вызываете мутацию, она создает копию базовой карты, и все последующие вызовы будут делегированы этой копии.
Этот подход следует использовать, если вы ожидаете, что некоторые клиенты изменят карту.
К сожалению, java не имеет такого CopyOnWriteMap
. Но вы можете найти стороннюю компанию или реализовать ее самостоятельно.
Наконец, вы должны помнить, что элементы на вашей карте могут быть изменены.
Ответ 5
Определенно возвращаем ImmutableMap, оправдание которого:
- Подпись метода (включая тип возврата) должна быть самодокументирована. Комментарии похожи на обслуживание клиентов: если ваши клиенты должны полагаться на них, тогда ваш основной продукт неисправен.
- Является ли что-то интерфейсом или классом, имеет значение только при расширении или его реализации. Учитывая экземпляр (объект), 99% клиентского кода времени не знают и не заботятся, является ли что-то интерфейсом или классом. Сначала я предположил, что ImmutableMap является интерфейсом. Только после того, как я нажал ссылку, я понял, что это класс.
Ответ 6
Это зависит от самого класса. Guava ImmutableMap
не предназначен для неизменяемого представления в изменяемом классе. Если ваш класс неизменен и имеет некоторую структуру, которая в основном представляет собой ImmutableMap
, тогда введите тип возврата ImmutableMap
. Однако, если ваш класс изменен, не делайте этого. Если у вас есть это:
public ImmutableMap<String, Integer> foo() {
return ImmutableMap.copyOf(internalMap);
}
Гуава будет копировать карту каждый раз. Это медленно. Но если internalMap
уже был ImmutableMap
, то он полностью прекрасен.
Если вы не ограничиваете свой класс возвратом ImmutableMap
, вы можете вместо этого вернуть Collections.unmodifiableMap
так:
public Map<String, Integer> foo() {
return Collections.unmodifiableMap(internalMap);
}
Обратите внимание, что это неизменный вид на карту. Если internalMap
изменяется, то будет сохранена кешированная копия Collections.unmodifiableMap(internalMap)
. Однако я все же предпочитаю его для геттеров.
Ответ 7
Это не отвечает на точный вопрос, но по-прежнему стоит подумать, нужно ли вообще возвращать карту. Если карта неизменна, то основной метод, который должен быть предоставлен, основан на get (key):
public Integer fooOf(String key) {
return map.get(key);
}
Это значительно упрощает API. Если на самом деле требуется карта, это может быть оставлено клиентом API путем предоставления потока записей:
public Stream<Map.Entry<String, Integer>> foos() {
map.entrySet().stream()
}
Затем клиент может создавать свою неизменную или изменяемую карту по мере необходимости или добавлять записи в свою собственную карту. Если клиент должен знать, существует ли значение, вместо него может быть возвращен необязательный вариант:
public Optional<Integer> fooOf(String key) {
return Optional.ofNullable(map.get(key));
}
Ответ 8
Неизменяемая карта - это тип Карты. Таким образом, оставляя возвращаемый тип Карты в порядке.
Чтобы гарантировать, что пользователи не будут изменять возвращаемый объект, документация метода может описывать характеристики возвращаемого объекта.
Ответ 9
Это, вероятно, вопрос мнения, но лучше понять, как использовать интерфейс для класса карты. Этот интерфейс не должен явно указывать, что он неизменен, но сообщение будет таким же, если вы не раскрываете какой-либо из методов setter родительского класса в интерфейсе.
Взгляните на следующую статью:
andy gibson