Почему многие классы Collection в Java расширяют абстрактный класс и также реализуют интерфейс? - PullRequest
41 голосов
/ 04 октября 2010

Почему многие классы Collection в Java расширяют класс Abstract, а также реализуют интерфейс (который также реализуется данным абстрактным классом)?

Например, класс HashSet расширяет AbstractSet и также реализует Set, но AbstractSet уже реализует Set.

Ответы [ 10 ]

32 голосов
/ 04 октября 2010

Это способ помнить, что этот класс действительно реализует этот интерфейс.
Это не будет иметь плохого эффекта, и это может помочь понять код, не проходя полную иерархию данного класса.

11 голосов
/ 04 октября 2010

С точки зрения системы типов классы не изменились бы, если бы они не реализовали интерфейс снова, поскольку абстрактные базовые классы уже реализуют их.

Это правда.

Причина, по которой они все-таки реализуют ее, - это (вероятно) в основном документация: HashSet is-a Set.И это делается явным путем добавления implements Set в конце, хотя это и не является строго обязательным.

Обратите внимание, что на самом деле различие наблюдается с помощью отражения, но мне было бы сложно создать некоторый код,перерыв, если HashSet не реализовал Set напрямую.

9 голосов
/ 13 апреля 2016

На практике это может не иметь большого значения, но я хотел уточнить, что явная реализация интерфейса не является точно такой же, как реализация его с помощью наследования. Разница присутствует в скомпилированных файлах классов и видна через отражение. Например.,

for (Class<?> c : ArrayList.class.getInterfaces())
    System.out.println(c);

Вывод показывает только интерфейсы явно , реализованные ArrayList, в том порядке, в котором они были записаны в источнике, который [в моей версии Java]:

interface java.util.List
interface java.util.RandomAccess
interface java.lang.Cloneable
interface java.io.Serializable

Выходные данные не включают интерфейсы, реализованные суперклассами, или интерфейсы, которые являются суперинтерфейсами тех, которые включены. В частности, Iterable и Collection отсутствуют в вышеприведенном, хотя ArrayList реализует их неявно. Чтобы найти их, вы должны рекурсивно повторить иерархию классов.

Было бы прискорбно, если какой-то код использует отражение и зависит от явной реализации интерфейсов, но это возможно, поэтому сопровождающие библиотеки коллекций могут неохотно менять ее сейчас, даже если они этого захотят. (Существует наблюдение под названием Закон Хайрама : «При достаточном количестве пользователей API не имеет значения, что вы обещаете в контракте; кто-то будет зависеть от всех наблюдаемых действий вашей системы») .)

К счастью, это различие не влияет на систему типов. Выражения new ArrayList<>() instanceof Iterable и Iterable.class.isAssignableFrom(ArrayList.class) по-прежнему оцениваются в true.

5 голосов
/ 04 октября 2010

В отличие от Колин Хеберт , я не покупаю тех людей, которые писали, которые заботились о читабельности. (Каждый, кто думает, что стандартные библиотеки Java были написаны безупречными богами, должен взглянуть на их источники. Когда я впервые сделал это, я пришел в ужас от форматирования кода и многочисленных вставленных копий блоков.)

Могу поспорить, что уже поздно, они устали и им было все равно.

2 голосов
/ 15 февраля 2018

Из «Эффективной Java» Джошуа Блоха:

Вы можете объединить преимущества интерфейсов и абстрактных классов, добавив абстрактный скелетный класс реализации для интерфейса.

Интерфейс определяет тип, возможно, предоставляя некоторые методы по умолчанию, в то время как скелетный класс реализует оставшиеся не примитивные методы интерфейса поверх примитивных методов интерфейса.Расширение скелетной реализации отнимает большую часть работы по реализации интерфейса.Это шаблонный метод шаблон.

По соглашению скелетные классы реализации называются AbstractInterface, где Interface - имя интерфейса, который они реализуют.Например:

AbstractCollection
AbstractSet
AbstractList
AbstractMap
1 голос
/ 05 октября 2010

Я также считаю, что это для ясности. Инфраструктура коллекций Java имеет довольно иерархическую структуру интерфейсов, которая определяет различные типы коллекций. Он начинается с интерфейса Collection, затем расширяется тремя основными подынтерфейсами Set, List и Queue. Существует также набор расширений SortedSet и очередь расширения BlockingQueue.

Теперь, конкретные классы, реализующие их, более понятны, если они явно указывают, какой интерфейс в реализуемой им иерархии, даже если он иногда выглядит избыточным. Как вы упомянули, класс, подобный HashSet, реализует Set, но класс, подобный TreeSet, хотя он также расширяет AbstractSet, реализует вместо этого SortedSet, который более специфичен, чем просто Set. HashSet может выглядеть излишним, но TreeSet это не так, потому что он требует реализации SortedSet. Тем не менее, оба класса являются конкретными реализациями и были бы более понятными, если бы оба следовали определенному соглашению в своем объявлении.

Существуют даже классы, которые реализуют более одного типа коллекции, например LinkedList, который реализует как List, так и Queue. Тем не менее, есть один класс, по крайней мере, немного «нетрадиционный», PriorityQueue. Он расширяет AbstractQueue, но явно не реализует Queue. Не спрашивай меня почему. :)

(ссылка на Java 5 API)

0 голосов
/ 13 июня 2017

Слишком поздно для ответа?

Я беру предположение, чтобы подтвердить свой ответ. Предположим, следующий код

HashMap extends AbstractMap (не реализует карту)

AbstractMap implements Map

Теперь представьте, что пришел какой-то случайный парень. Измененный реализует Map для некоторого java.util.Map1 с точно таким же набором методов, что и Map

.

В этой ситуации не будет никаких ошибок компиляции, и jdk будет скомпилирован (вне курса тест не пройдёт и поймает это).

Теперь любой клиент, использующий HashMap в качестве Map m = new HashMap (), начнет отказывать. Это намного ниже по течению.

Поскольку и AbstractMap, Map и т. Д. Происходят из одного и того же продукта, следовательно, этот аргумент выглядит детским (который, по всей вероятности, является или может не быть), но подумайте о проекте, в котором базовый класс взят из другой библиотеки jar / третьей стороны. и т. д. Затем третья сторона / другая команда может изменить свою базовую реализацию.

Реализуя также «интерфейс» в классе Child, разработчик пытается сделать класс самодостаточным, стойким к поломке API.

0 голосов
/ 27 ноября 2015

На мой взгляд, когда класс реализует интерфейс, он должен реализовать все методы, присутствующие в нем (так как по умолчанию они являются публичными и абстрактными методами в интерфейсе).

Если мы не хотимреализовать все методы интерфейса, это должен быть абстрактный класс.

Так что здесь, если некоторые методы уже реализованы в каком-то абстрактном классе, реализующем определенный интерфейс, и мы должны расширить функциональность для других методов, которые не были реализованы,необходимо снова реализовать оригинальный интерфейс в нашем классе, чтобы получить те оставшиеся наборы методов. Это поможет в поддержании договорных правил, установленных интерфейсом.

Это приведет к доработке, если будет реализовывать только интерфейс, и снова переопределитвсе методы с определениями методов в нашем классе.

0 голосов
/ 04 октября 2010

Я полагаю, что может быть другой способ обработки членов набора, интерфейс, даже если реализация операции по умолчанию не служит универсальной для всех.Циклическая очередь против очереди LIFO может реализовывать один и тот же интерфейс, но их конкретные операции будут реализованы по-разному, верно?

0 голосов
/ 04 октября 2010

Если бы у вас был только абстрактный класс, вы не могли бы создать собственный класс, который наследуется и от другого класса.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...