Что (не) объявлять при реализации интерфейса с абстрактным классом?

У меня есть интерфейс A, для которого я должен предоставить несколько разных реализации. Однако эти реализации имеют общие вспомогательные методы, поэтому Я переместил эти методы в абстрактный базовый класс.

Interface A {
    void doX();
}

abstract Class B implements A {
    protected void commonY() {
        // ...
    }

    @Override
    public abstract void doX();
}

Class C extends B {
    @Override
    public void doX() {
        // ...
    }
}

Class D extends B {
    @Override
    public void doX() {
        // ...
    }
}

Мой код работает должным образом, но у меня есть несколько вопросов:

  • Должен ли я объявить абстрактный метод doX () в классе B? Почему нет)?

  • Должен ли я также явно объявить «реализует A» для классов C и D? Почему нет)?

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
7
0
1 005
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

  • Должен ли я объявить абстрактный метод doX () в классе B? Почему нет)?

Нет. Это абстрактный класс - определение интерфейса будет означать, что все подклассы должны будут реализовать эти методы. Другими словами, это избыточно.

  • Должен ли я также явно объявить «реализует A» для классов C и D? Почему нет)?

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

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

Наследование переходное. Вам не нужно писать, что класс C реализует интерфейс A, если класс C является производным от класса B, который реализует интерфейс A. Однако это тоже не сильно повредит.

Я не думаю, что абстрактный класс должен реализовывать каждый метод в интерфейсе. Я только что протестировал это, и кажется, что вы можете реализовать интерфейс в абстрактном классе, фактически не объявляя какие-либо методы интерфейса.

user7094 09.12.2008 14:48

Хм, наверное, ты прав. Я терял память о Java и о моем текущем опыте работы с C#. В C# абстрактный класс, реализующий интерфейс, требуется для реализации всех методов интерфейса (даже просто помечая их как абстрактные).

yfeldblum 09.12.2008 15:16

Я бы не объявлял doX() в B и не добавлял «implements A» в C и D, потому что вы должны не повторяйся.

Абстрактный doX() в B ничего не добавляет, поскольку он уже указан в "implements A". То же самое верно для добавления «implements A» к C и D.

Единственное возможное использование этих пунктов - документация: если вы хотите четко указать, что C (или D) - это A, то вы можете добавить implements, но вы должны знать, что это действительно не имеет значения. компилятор.

Ответ принят как подходящий

Думаю, лучше было бы сделать так:

Interface A {
        void doX();
}

abstract Class B {
        protected void commonY() {
                // ...
        }
}

Class C extends B implements A{

        public void doX() {
                // ...
        }
}

Class D extends B implements A{

        public void doX() {
                // ...
        }
}

Не следует путать интерфейс (сигнатуру методов) с реализацией.

Преимущество этого решения заключается в том, что когда вводится класс X, реализующий Z, но требующий функциональности метода commonY, он может специализироваться на классе B.

Nick Holt 09.12.2008 14:59

Это будет зависеть от использования. Если B является базой для нескольких реализаций A, тогда он должен реализовывать A. То же, что и AbstractList или AbstractCollection.

Robin 09.12.2008 21:43

Я просто добавлю другой вариант.

Превратите абстрактный класс B в класс AUtil, который не реализует A. Для сигнатур методов может потребоваться дополнительный аргумент типа A для работы.

C и D реализуют A и создают экземпляр AUtil внутри. Это позволяет C и D расширять другие классы.

Я бы точно так и поступил. Я категорически против использования наследования ради предоставления методов. Вот для чего нужны статические служебные методы!

jdmichal 13.03.2009 08:21

Я согласен с JeeBee: подумайте о реализации ваших вспомогательных методов где-нибудь, кроме абстрактного базового класса.

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

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

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

Другие вопросы по теме