Я слышал аргумент, что вы должны использовать самый общий доступный интерфейс, чтобы вы не были привязаны к конкретной реализации этого интерфейса. Применяется ли эта логика к интерфейсам, таким как java.util.Collection ?
Я бы предпочел увидеть что-то вроде следующего:
List<Foo> getFoos()
или же
Set<Foo> getFoos()
вместо
Collection<Foo> getFoos()
В последнем случае я не знаю, с каким набором данных я имею дело, тогда как в первых двух случаях я могу сделать некоторые предположения о порядке и уникальности. Есть ли полезность java.util.Collection помимо того, что он является логическим родителем для наборов и списков?
Если вы сталкивались с кодом, который использовал Collection при выполнении обзора кода, как бы вы определили, является ли его использование оправданным, и какие предложения вы бы предложили для его замены более конкретным интерфейсом?
Collection<List<?>>
? Разговор о кодировании ужасов!Ответы:
Абстракции живут дольше реализаций
В общем, чем абстрактнее ваш дизайн, тем дольше он, вероятно, останется полезным. Таким образом, поскольку Collection более абстрактен в том, что он является подчиненным интерфейсом, тогда дизайн API на основе Collection с большей вероятностью останется полезным, чем дизайн на основе List.
Однако общий принцип заключается в том, чтобы использовать наиболее подходящую абстракцию . Таким образом, если ваша коллекция должна поддерживать упорядоченные элементы, тогда нужно назначить список, если дубликатов не должно быть, то и набор, и так далее.
Замечание об общем дизайне интерфейса
Поскольку вы заинтересованы в использовании интерфейса Collection с генериками, вам может быть полезно следующее. «Эффективная Java » Джошуа Блоха рекомендует следующий подход при разработке интерфейса, который будет опираться на дженерики: продюсеры расширяют, потребители супер
Это также известно как правило PECS . По сути, если в ваш класс передаются универсальные коллекции, производящие данные, подпись должна выглядеть следующим образом:
Таким образом, тип ввода может быть E или любым подклассом E (E определяется как супер- и подкласс самого себя в языке Java).
И наоборот, универсальная коллекция, которая передается для использования данных, должна иметь такую подпись:
Метод будет корректно работать с любым суперклассом E. В целом, использование этого подхода сделает ваш интерфейс менее удивительным для ваших пользователей, потому что вы сможете пройти
Collection<Number>
иCollection<Integer>
правильно их обработать.источник
Collection
Интерфейс, и наиболее разрешающая формаCollection<?>
, отлично подходит для параметров , которые вы принимаете. Основанный на использовании в самой библиотеке Java, он более распространен как тип параметра, чем как тип возвращаемого значения.Что касается типов возврата, я думаю, что ваша точка зрения верна: если от людей ожидают, что они получат к ней доступ, они должны знать порядок (в смысле Big-O) выполняемой операции. Я бы перебрал
Collection
возвращаемый результат и добавил его в другую коллекцию, но было бы немного глупо вызыватьcontains
его, не зная, является ли это операцией O (1), O (log n) или O (n). Конечно, просто потому, что у вас естьSet
хэш-набор или отсортированный набор, но в какой-то момент вы сделаете предположение, что интерфейс был реализован разумно (и тогда вам нужно будет перейти к плану B, если ваше предположение показывается неверным).Как упоминает Том, иногда вам нужно возвращать a
Collection
для поддержания инкапсуляции: вы не хотите, чтобы детали реализации просачивались, даже если вы могли бы вернуть что-то более конкретное. Или в случае, упомянутом Томом, вы можете вернуть более конкретный контейнер, но тогда вам придется его создать.источник
Я смотрю на это с совершенно противоположной точки зрения и спрашиваю:
Это довольно легко оправдать. Список используется, когда вам нужны некоторые функции, которые не предлагаются коллекцией. Если вам не нужна эта дополнительная функциональность - какое у вас обоснование? (И я не буду покупать, «Я бы предпочел увидеть это»)
Есть много случаев, когда вы будете использовать коллекцию в целях только для чтения, заполняя ее все сразу, полностью повторяя ее - вам когда-нибудь нужно индексировать вещь вручную?
Чтобы привести реальный пример. Скажем, я выполняю простой запрос к базе данных. (
SELECT id,name,rep FROM people WHERE name LIKE '%squared'
) Я собираю соответствующие данные, заполняю объекты Person и помещаю их в PersonList)Итак, какое оправдание я буду иметь для этих дополнительных методов? (который в любом случае не будет реализован в моем PersonList)
источник