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

87

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

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

О: Если мы используем шаблонный метод проектирования.

Я прав ?

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

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

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

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

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

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

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

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

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

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

Ученик
источник
возможный дубликат интерфейса и абстрактного класса (общий объектно-
ориентированный подход
Это не дубликат. OP хочет знать, когда расширять абстрактный класс, а не реализовывать интерфейс. Он не хочет знать, когда писать абстрактный класс или интерфейс. Его абстрактный класс и интерфейс уже написаны. Hd хочет знать, расширять или внедрять.
Шиплу Мокаддим
1
@ shiplu.mokadd.im Это различие без разницы. Вы не можете использовать абстрактный класс, не расширяя его. Ваши придирки здесь кажутся совершенно бессмысленными.
Marquis of Lorne

Ответы:

86

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

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

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

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

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

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

Божественная Пустыня
источник
Я думаю, OP хочет знать, когда расширять абстрактный класс, а не реализовывать интерфейс
Шиплу Мокаддим
@ shiplu.mokadd.im На самом деле ОП задал очень конкретный вопрос, на который ответ был либо «да», либо «нет».
Marquis of Lorne
4
Ты прав. Но в SO мы отвечаем да / нет с соответствующими объяснениями.
Шиплу Мокаддим
1
@ shiplu.mokadd.im Я не понимаю, как это дает вам право неверно сформулировать его вопрос.
Marquis of Lorne
Только на основании этого единственного заявления If we are using template method design patternМы не можем сказать YESилиNO
DivineDesert
31

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

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

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

Интерфейс:

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

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

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

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

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

Жером Верстринж
источник
3
В Java 8 для интерфейса # 8, вы можете иметь defaultи staticметоды тоже.
Начинающий пользователь
15

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

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

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

SMK
источник
9

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

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

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()нереализованные

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()не является частью интерфейса. Помните, что интерфейс определяет самые общие компоненты торговой системы. Другой инженер может разработать СОВЕРШЕННО ДРУГУЮ торговую систему, в которой он не заботится о поиске хороших сделок. Интерфейс гарантирует, что отдел продаж также может взаимодействовать со своей системой, поэтому желательно не связывать ваш интерфейс с концепциями приложения, такими как «выгодные предложения».

Адам Хьюз
источник
6

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

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

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

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

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

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

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

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

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

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

Santh
источник
4

Вещи сильно изменились за последние три года с добавлением новых возможностей интерфейса с версией Java 8.

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

Интерфейс против абстрактного класса (общий объектно-ориентированный подход)

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

Подводя итог: теперь баланс больше смещается в сторону интерфейсов .

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

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

Шаблоны создания:

Abstract_factory_pattern

Структурные образцы:

Decorator_pattern

Модели поведения:

Mediator_pattern

Равиндра бабу
источник
Это: «Абстрактный класс устанавливает» - это «связь между связанными классами, и интерфейс предоставляет« возможность между несвязанными классами ».
Габриэль
3

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

Маркиз Лорн
источник
1
Если вы не похожи на расплывчатые; Используйте интерфейс всякий раз, когда можете;)
Питер Лоури,
@PeterLawrey Да, не позволяйте круговым аргументам тормозить вас ;-)
Маркиз Лорн,
В конце концов, это «переполнение стека». ;) Я хочу сказать, что если вы можете использовать более простой интерфейс, сделайте это. В противном случае у вас нет выбора, кроме как использовать абстрактный класс. Я не считаю это очень сложным.
Питер Лоури
Я думаю, вы могли бы предложить более конструктивную идею. как поговорить о некоторых репрезентативных сценариях /
Adams.H
3

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

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

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

Шиплу Мокаддим
источник
Дополнительная ссылка на Pure Virtual Function добавит больше информации о конвергенции абстрактных классов и интерфейсов , Pure virtual functions can also be used where the method declarations are being used to define an interface - similar to what the interface keyword in Java explicitly specifies. In such a use, derived classes will supply all implementations. In such a design pattern, the abstract class which serves as an interface will contain only pure virtual functions, but no data members or ordinary methods. часть (1/2)
Abhijeet
Часть (2/2) Расхождение между абстрактным классом и интерфейсом объясняется последней строкой выше no data members or ordinary methods[в абстрактном классе].
Abhijeet
3

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

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

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

user2794921
источник
2

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

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

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

Шаронда Уокер
источник
0

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

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

Рамеш ПВК
источник
0

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

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

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

Интерфейсы:

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

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

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

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

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

пользователь7439667
источник