#java #interface #polymorphism #abstract-class #guava
Вопрос:
Java позволяет мне определить реализацию интерфейса, которая переопределяет его методы методами, возвращающими подклассы возвращаемого типа интерфейса. Например:
public interface GoblinHorde {
Collection<Goblin> getGoblins();
}
public final class UniqueGoblinHorde implements GoblinHorde {
private Set<Goblins> currentGoblins = new HashSet<>();
@Override
public Set<Goblin> getGoblins() {
return currentGoblins;
}
}
Для меня это имеет смысл, потому что это дает разработчикам возможность найти компромисс между удобством обслуживания кода и производительностью. Если вы собираетесь использовать результаты GoblinHorde#getGoblins()
в качестве набора, у вас должна быть возможность объявить тип вашего GoblinHorde как impl вместо интерфейса. Это позволяет использовать возвращенный набор вместо ненужного создания нового набора из коллекции, которая уже является набором. Очевидно, что это происходит за счет кода, который в долгосрочной перспективе менее удобен для обслуживания, но бывают ситуации, когда это имеет смысл.
Однако Google, похоже, не всегда следует этой схеме. AbstractSetMultimap#asMap()
Метод Guava 30.1.1 возвращает более общий Map<K, Collection<V>>
вместо Map<K, Set<V>>
и заходит так далеко, чтобы включить в Javadocs примечание, которое гласит: Note: The returned map's values are guaranteed to be of type Set. To obtain this map with the more specific generic type Map<K, Set<V>>, call Multimaps.asMap(SetMultimap) instead.
В чем причина необходимости использования вторичного статического вспомогательного метода?
Комментарии:
1. На самом деле невозможно переопределить
asMap()
возвратMap<K, Set<V>>
. Java сообщит, что это недопустимое переопределение.
Ответ №1:
Единственная причина, по которой я мог видеть, заключается в том, что ковариантные типы возврата не поддерживались до версии Java 1.5, выпущенной в сентябре 2004 года, поэтому тогда было невозможно вернуть более конкретный тип. Не уверен, когда была написана эта библиотека, но я могу только предположить, что она была написана, когда это не поддерживалось.
Комментарии:
1. Это тоже было бы очень веской причиной, я согласен.
Ответ №2:
Я вижу единственную причину: они хотят сохранить возможность изменения реализации, чтобы List
, например, возвращать a Set
вместо a, и избежать взлома многих приложений, полагающихся на возвращаемый объект Map<K, Set<V>>
. Конечно, это гипотетическое изменение, скорее всего, будет означать, что ваш код потребуется изменить, чтобы скорректировать вашу бизнес-логику (возможно, вы полагались на «функцию» без дубликатов» Set
), но, по крайней мере, ваш код все равно будет компилироваться (если вы не выполните какое-либо приведение).
Комментарии:
1. Они должны были вернуться
Set<V>
тогда, потому что документация является такой же частью контракта, как и статические типы. Утверждение, что он «гарантированно» будет иметь типSet
, не делая его гарантией, обеспечиваемой компилятором, только открывает дверь для случайных нарушений контракта.2. Я согласен с вами, я просто говорю, что оставить дверь открытой для будущих изменений-единственная правдоподобная причина, о которой я могу думать (возможно, слишком экстремальная практика «программы для интерфейсов, а не реализации». Да
Set
, это все еще интерфейс, ноCollection
это суперинтерфейсы, вы понимаете мою точку зрения).3. Ну, я хочу сказать, что дверь не открыта для будущих изменений, потому что они сделали «гарантированный возврат комплекта» частью своего контракта с помощью документации. Изменение его было бы таким же серьезным изменением, как если бы оно было правильно набрано.
4. Как бы. Если вы на самом деле не приведете возвращаемый объект к
Map<K, Set<V>>
определенным функциям или не будете полагаться наSet
них , возможно, изменение отSet
доList
не повлияет на ваш вариант использования. Опять же, я не говорю, что согласен с их подходом. Я просто говорю, что для этого может быть резон, и это единственное, что я могу придумать.