Абстрактный класс против интерфейса в Java

Мне задали вопрос, я хотел, чтобы мой ответ был рассмотрен здесь.

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

A: Если мы используем шаблон дизайна шаблона.

Я прав?

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

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

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

3) мы можем расширить максимум одного абстрактного класса, но можем реализовать более одного интерфейса

Повторяя вопрос: существуют ли какие-либо другие сценарии, кроме упомянутых выше, где конкретно нам требуется использовать абстрактный класс (можно увидеть, шаблонный шаблонный метод проектирования концептуально основан только на этом)?

Интерфейс против абстрактного класса

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

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

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

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

16 ответов

Когда использовать интерфейсы

Интерфейс позволяет кому-то начинать с нуля, чтобы реализовать ваш интерфейс или внедрить ваш интерфейс в каком-то другом коде, исходная или основная цель которого сильно отличалась от вашего интерфейса. Для них ваш интерфейс только случайный, то, что нужно добавить в их код, чтобы иметь возможность использовать ваш пакет. Недостатком является то, что каждый метод в интерфейсе должен быть публичным. Вы можете не хотеть выставлять все.

Когда использовать абстрактные классы

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

Когда использовать оба

Вы можете предложить лучшее из обоих миров, интерфейс и абстрактный класс. Реализаторы могут игнорировать ваш абстрактный класс, если захотят. Единственный недостаток - вызов методов через имя их интерфейса немного медленнее, чем вызов через абстрактное имя класса.

повторяя вопрос: есть какой-то другой сценарий, кроме упомянутых выше, где конкретно нам требуется использовать абстрактный класс (можно видеть, что шаблон проектирования шаблонного метода концептуально основан только на этом)

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

Из личного поста в блоге:

Интерфейс:

  1. Класс может реализовывать несколько интерфейсов
  2. Интерфейс не может предоставить какой-либо код вообще
  3. Интерфейс может определять только открытые статические конечные константы.
  4. Интерфейс не может определять переменные экземпляра
  5. Добавление нового метода оказывает волновое влияние на реализацию классов (сопровождение проекта)
  6. JAXB не может иметь дело с интерфейсами
  7. Интерфейс не может расширять или реализовывать абстрактный класс
  8. Все методы интерфейса общедоступны

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

Абстрактный класс:

  1. Класс может расширять не более одного абстрактного класса
  2. Абстрактный класс может содержать код
  3. Абстрактный класс может определять как статические, так и константы экземпляра (финал)
  4. Абстрактный класс может определять переменные экземпляра
  5. Модификация существующего кода абстрактного класса оказывает волновое влияние на расширение классов (поддержка реализации)
  6. Добавление нового метода в абстрактный класс не влияет на расширение классов.
  7. Абстрактный класс может реализовывать интерфейс
  8. Абстрактные классы могут реализовывать закрытые и защищенные методы.

Абстрактные классы должны использоваться для (частичной) реализации. Они могут быть средством ограничения способа реализации контрактов API.

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

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

Взгляните на статью: http://shoaibmk.blogspot.com/2011/09/abstract-class-is-class-which-cannot-be.html

Здесь много хороших ответов, но я часто нахожу использование ОБА интерфейсов, и абстрактные классы - лучший путь. Рассмотрим этот надуманный пример:

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

1) Trading system places orders
2) Trading system receives acknowledgements

и может быть захвачен в интерфейсе, ITradeSystem

public interface ITradeSystem{

     public void placeOrder(IOrder order);
     public void ackOrder(IOrder order);

}

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

Итак, вы идете дальше и строите систему для биржевых трейдеров; они слышали, что в вашей системе есть функция поиска дешевых акций, и очень хотят ее опробовать! Вы фиксируете это поведение в методе findGoodDeals(), но также осознайте, что при подключении к рынкам возникает много грязных вещей. Например, вы должны открыть SocketChannel,

public class StockTradeSystem implements ITradeSystem{    

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

Конкретные реализации будут иметь много таких грязных методов, как connectToMarket(), но findGoodDeals() это все трейдеры на самом деле заботятся.

Теперь вот где абстрактные классы вступают в игру. Ваш босс сообщает вам, что валютные трейдеры также хотят использовать вашу систему. И, глядя на валютные рынки, вы видите, что сантехника почти идентична фондовым рынкам. По факту, connectToMarket() может быть повторно использован дословно для подключения к валютным рынкам. Тем не мение, findGoodDeals() это совершенно другое понятие на валютной арене. Поэтому, прежде чем передать кодовую базу ребёнку, работающему с иностранной валютой через океан, вы сначала сделаете рефакторинг в abstract класс, уходя findGoodDeals() unimplmented

public abstract class ABCTradeSystem implements ITradeSystem{    

    public abstract void findGoodDeals();

    @Override 
    public void placeOrder(IOrder order);
         getMarket().place(order);

    @Override 
    public void ackOrder(IOrder order);
         System.out.println("Order received" + order);    

    private void connectToMarket();
       SocketChannel sock = Socket.open();
       sock.bind(marketAddress); 
       <LOTS MORE MESSY CODE>
    }

Ваша система торговли акциями реализует findGoodDeals() как вы уже определили,

public class StockTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       deals = <apply magic wizardry>
       System.out.println("The best stocks to buy are: " + deals);
    }

но теперь одаренный FX может построить свою систему, просто обеспечив реализацию findGoodDeals() для валют; ей не нужно переопределять сокетные соединения или даже методы интерфейса!

public class CurrencyTradeSystem extends ABCTradeSystem{    

    public void findGoodDeals();
       ccys = <Genius stuff to find undervalued currencies>
       System.out.println("The best FX spot rates are: " + ccys);
    }

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

Примечание: можно задаться вопросом, почему findGreatDeals() не является частью интерфейса. Помните, что интерфейс определяет самые общие компоненты торговой системы. Другой инженер может разработать ПОЛНОСТЬЮ РАЗЛИЧНУЮ торговую систему, где он не заботится о поиске хороших сделок. Интерфейс гарантирует, что отдел продаж также может взаимодействовать со своей системой, поэтому желательно не связывать ваш интерфейс с такими понятиями приложения, как "выгодные предложения".

Что вы должны использовать, абстрактные классы или интерфейсы?

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

Вы хотите поделиться кодом между несколькими тесно связанными классами.

Вы ожидаете, что классы, которые расширяют ваш абстрактный класс, имеют много общих методов или полей или требуют модификаторов доступа, отличных от public (например, protected и private).

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

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

Вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, интерфейсы Comparable и Cloneable реализованы многими несвязанными классами.

Вы хотите указать поведение определенного типа данных, но не беспокоитесь о том, кто реализует его поведение.

Вы хотите воспользоваться множественным наследованием типа.

http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

За последние три года многое изменилось с добавлением новых возможностей для взаимодействия с выпуском Java 8.

Со страницы документации Oracle на интерфейсе:

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

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

Еще одно соображение, чтобы предпочесть абстрактный класс интерфейсу:

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

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


Что касается второй части вашего вопроса, которая действительна для большинства языков программирования, включая Java до выпуска Java-8

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

Вы не можете пойти и изменить интерфейс без необходимости изменять много других вещей в вашем коде

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

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

Чтобы выбрать один из них между интерфейсом и абстрактным классом, укажите на странице документации оракула следующее:

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

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

Обратитесь к этим связанным вопросам для более подробной информации:

Интерфейс против абстрактного класса (общий ОО)

Как мне объяснить разницу между интерфейсом и абстрактным классом?

В итоге: баланс все больше склоняется к интерфейсам.

Существуют ли какие-либо другие сценарии, кроме упомянутых выше, где конкретно нам требуется использовать абстрактный класс (можно увидеть, что шаблонный шаблонный метод концептуально основан только на этом)?

Некоторые шаблоны проектирования используют абстрактные классы (поверх интерфейсов) отдельно от шаблона метода Template.

Творческие образцы:

https://en.wikipedia.org/wiki/Abstract_factory_pattern

Структурные схемы:

Decorator_pattern

Поведенческие модели:

Mediator_pattern

Кратчайший ответ - расширить абстрактный класс, если в нем уже реализованы некоторые функции, которые вы ищете.

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

В шаблоне дизайна шаблона должно быть определено поведение. Это поведение зависит от других методов, которые являются абстрактными. Делая подкласс и определяя эти методы, вы фактически определяете основное поведение. Базовое поведение не может быть в интерфейсе, так как интерфейс ничего не определяет, он просто объявляет. Таким образом, шаблон дизайна шаблона всегда поставляется с абстрактным классом. Если вы хотите сохранить поток поведения без изменений, вы должны расширить абстрактный класс, но не переопределять основное поведение.

Вы не правы. Есть много сценариев. Просто невозможно свести его к одному правилу из 8 слов.

На мой взгляд, основное отличие заключается в том, что an interface can't contain non abstract methods while an abstract class can, Таким образом, если подклассы имеют общее поведение, это поведение может быть реализовано в суперклассе и, таким образом, унаследовано в подклассах.

Также я процитировал следующее из книги "Разработка архитектуры программного обеспечения в java"

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

Абстрактные классы отличаются от интерфейсов в двух важных аспектах

  • они обеспечивают реализацию по умолчанию для выбранных методов (что покрывается вашим ответом)
  • абстрактные классы могут иметь состояние (переменные экземпляра) - так что это еще одна ситуация, когда вы хотите использовать их вместо интерфейсов

Использование аннотации и интерфейса:

У одного есть отношения "есть отношения", а у другого есть отношения "есть отношения"

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

Пример: -> В людях у нас есть некоторые свойства по умолчанию: еда, сон и т. Д., Но если у кого-то есть какие-либо другие учебные занятия, такие как плавание, игры и т. Д., Они могут быть выражены через интерфейс.

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

Это мое понимание, надеюсь, это поможет

Абстрактные классы:

  1. Может иметь переменные-члены, которые наследуются (не может быть сделано в интерфейсах)
  2. Может иметь конструкторы (интерфейсы не могут)
  3. Его методы могут иметь любую видимость (то есть: приватные, защищенные и т. Д., В то время как все методы интерфейса являются открытыми)
  4. Может иметь определенные методы (методы с реализацией)

Интерфейсы:

  1. Могут иметь переменные, но все они являются открытыми статическими конечными переменными
    • постоянные значения, которые никогда не изменяются со статической областью действия
    • Нестатические переменные требуют экземпляра, и вы не можете создать экземпляр интерфейса
  2. Все методы являются абстрактными (без кода в абстрактных методах)
    • весь код должен быть фактически написан в классе, который реализует определенный интерфейс

Abstract classes should be extended when you want to some common behavior to get extended, Абстрактный суперкласс будет иметь общее поведение и будет определять абстрактный метод / специфическое поведение, которое должны реализовывать подклассы.

Interfaces allows you to change the implementation anytime allowing the interface to be intact,

Я думаю, что в ответах здесь отсутствует главное:

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

Это похоже на черты PHP или утиную типизацию Python.

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

Разница между абстрактным классом и интерфейсом

Абстрактный класс

  • Абстрактный класс может иметь абстрактные и неабстрактные методы
  • абстрактный класс может содержать не конечные переменные
  • Абстрактный класс может иметь конечные, не финальные, статические и нестатические переменные
  • Абстрактный класс может обеспечить реализацию интерфейса
  • Абстрактный класс может быть расширен с помощью ключевого слова "extends"
  • Абстрактный класс может расширять другой класс Java и реализовывать несколько интерфейсов Java
  • Абстрактный класс Java может иметь такие члены класса, как private, protected и т. Д.

Интерфейсы

  • Интерфейс может иметь только абстрактные методы
  • Переменные, объявленные в интерфейсе Java, по умолчанию являются окончательными
  • Интерфейс имеет только статические и конечные переменные
  • Интерфейс не может обеспечить реализацию абстрактного класса
  • Интерфейс Java может быть реализован с использованием ключевого слова "Implements"
  • Интерфейс может расширять только другой интерфейс Java
  • Члены интерфейса Java являются общедоступными по умолчанию
Другие вопросы по тегам