Допустим, я пишу метод, который должен возвращать Map . Например:
public Map<String, Integer> foo() {
return new HashMap<String, Integer>();
}
Поразмыслив над этим некоторое время, я решил, что нет причин изменять эту карту после ее создания. Таким образом, я хотел бы вернуть ImmutableMap .
public Map<String, Integer> foo() {
return ImmutableMap.of();
}
Следует ли мне оставить тип возвращаемого значения как общую карту или указать, что я возвращаю ImmutableMap?
С одной стороны, именно для этого были созданы интерфейсы; чтобы скрыть детали реализации.
С другой стороны, если я оставлю это так, другие разработчики могут упустить тот факт, что этот объект неизменяем. Таким образом, я не достигну главной цели неизменяемых объектов; чтобы сделать код более понятным за счет минимизации количества объектов, которые могут изменяться. Хуже того, через некоторое время кто-то может попытаться изменить этот объект, и это приведет к ошибке выполнения (компилятор не предупредит об этом).
return ImmutableMap.of(somethingElse)
. Вместо этого вы должны сохранитьImmutableMap.of(somethingElse)
как поле и вернуть только это поле. Все это здесь влияет на дизайн.Ответы:
Если вы пишете общедоступный API и эта неизменяемость является важным аспектом вашего дизайна, я определенно сделаю это явным: либо имя метода явно указывает на то, что возвращаемая карта будет неизменной, либо путем возврата конкретного типа карта. На мой взгляд, упоминания об этом в javadoc недостаточно.
Поскольку вы, очевидно, используете реализацию Guava, я просмотрел документ, и это абстрактный класс, поэтому он дает вам немного гибкости в отношении фактического, конкретного типа.
Если вы пишете внутренний инструмент / библиотеку, становится гораздо более приемлемым просто вернуть простой
Map
. Люди будут знать внутреннее устройство кода, который они вызывают, или, по крайней мере, будут иметь легкий доступ к нему.Я пришел к выводу, что явное - это хорошо, не оставляйте все на волю случая.
источник
Map
тип интерфейса илиImmutableMap
тип реализации.ImmutableMap
, команда Guava советует рассмотретьImmutableMap
интерфейс с семантическими гарантиями и вам следует возвращать этот тип напрямую.У вас должен быть
ImmutableMap
тип возврата.Map
содержит методы, которые не поддерживаются в реализацииImmutableMap
(напримерput
) и отмеченные@deprecated
вImmutableMap
.Использование устаревших методов приведет к предупреждению компилятора, и большинство IDE будут предупреждать, когда люди пытаются использовать устаревшие методы.
Это расширенное предупреждение предпочтительнее наличия исключений во время выполнения в качестве первого намёка на то, что что-то не так.
источник
Collections.immutableMap
тогда?List
, нет никакой гарантииList::add
. ПопробуйArrays.asList("a", "b").add("c");
. Нет никаких указаний на то, что он выйдет из строя во время выполнения, но это так. По крайней мере, Гуава предупреждает вас.Вы должны упомянуть об этом в javadocs. Знаете, разработчики их читают.
Ни один разработчик не публикует свой код непроверенным. И когда он это проверяет, он получает исключение, в котором он не только видит причину, но также файл и строку, в которых он пытался записать на неизменяемую карту.
Обратите внимание, что
Map
неизменным будет только сам объект, а не содержащиеся в нем объекты.источник
Это правда, но другим разработчикам следует протестировать свой код и убедиться, что он покрыт.
Тем не менее, у вас есть еще 2 варианта решения этой проблемы:
Использовать 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
Копирование при записи коллекций обычно используется, когда вам приходится иметь дело с
параллелизмом. Но эта концепция также поможет вам в вашей ситуации, поскольку CopyOnWriteMap создает копию внутренней структуры данных при изменении операции (например, добавление, удаление).
В этом случае вам понадобится тонкая оболочка вокруг вашей карты, которая делегирует все вызовы методов базовой карте, кроме изменяющих операций. Если вызывается изменяющая операция, она создает копию базовой карты, и все дальнейшие вызовы будут делегированы этой копии.
Этот подход следует использовать, если вы ожидаете, что некоторые клиенты изменят карту.
К сожалению, в java нет такого
CopyOnWriteMap
. Но вы можете найти третью сторону или реализовать это самостоятельно.Наконец, вы должны иметь в виду, что элементы на вашей карте все еще могут быть изменяемыми.
источник
Определенно верните ImmutableMap, обоснование:
источник
Это зависит от самого класса. 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)
. Однако я все еще предпочитаю его для геттеров.источник
unmodifiableMap
может быть изменен только держателем ссылки - это вид, и держатель вспомогательной карты может изменить вспомогательную карту, а затем вид изменится. Так что это важное соображение.Collections.unmodifiableMap
. Этот метод возвращает вид на исходную карту, а не создает отдельный отдельный новый объект карты. Таким образом, любой другой код, ссылающийся на исходную карту, может ее изменить, а затем владелец якобы неизменяемой карты на собственном опыте узнает, что карта действительно может быть изменена из-под него. Я сам был укусил этим фактом. Я научился либо возвращать копию Карты, либо использовать ГуавуImmutableMap
.Это не ответ на точный вопрос, но все же стоит подумать, нужно ли вообще возвращать карту. Если карта неизменна, то предоставляемый основной метод основан на get (key):
public Integer fooOf(String key) { return map.get(key); }
Это делает API намного более жестким. Если карта действительно требуется, это можно оставить на усмотрение клиента API, предоставив поток записей:
public Stream<Map.Entry<String, Integer>> foos() { map.entrySet().stream() }
Затем клиент может создать свою собственную неизменяемую или изменяемую карту по своему усмотрению или добавить записи в свою собственную карту. Если клиенту необходимо знать, существует ли значение, вместо него можно вернуть optional:
public Optional<Integer> fooOf(String key) { return Optional.ofNullable(map.get(key)); }
источник
Неизменяемая карта - это разновидность карты. Так что оставить возвращаемый тип Map - это нормально.
Чтобы гарантировать, что пользователи не изменят возвращаемый объект, документация метода может описывать характеристики возвращаемого объекта.
источник
Возможно, это вопрос личного мнения, но здесь лучше использовать интерфейс для класса карты. Этот интерфейс не должен явно указывать, что он неизменяемый, но сообщение будет таким же, если вы не предоставите ни один из методов установки родительского класса в интерфейсе.
Взгляните на следующую статью:
Энди Гибсон
источник
Map
интерфейс, является то, что вы не можете передать это какой-либо функции API, ожидающей a,Map
без ее преобразования. Сюда входят всевозможные копи-конструкторы других типов карт. В дополнение к этому, если изменчивость только «скрыта» интерфейсом, ваши пользователи всегда могут обойти это, преобразовав и взломав ваш код таким образом.