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


88

Мені задали питання, я хотів, щоб моя відповідь була розглянута тут.

З: В якому сценарії доцільніше розширити абстрактний клас, а не реалізовувати інтерфейс (и)?

В: Якщо ми використовуємо шаблонний шаблон дизайну методу.

Я прав?

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

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

2) використовувати інтерфейс, якщо вам потрібно поставити підпис однаковим (а реалізація різним), щоб ви могли відповідати реалізації інтерфейсу

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

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

Інтерфейс проти абстрактного класу

Вибір між цими двома насправді залежить від того, що ви хочете зробити, але, на щастя для нас, Еріх Гамма може нам трохи допомогти.

Як завжди є компроміс, інтерфейс дає вам свободу щодо базового класу, абстрактний клас дає вам свободу додавати нові методи пізніше . - Еріх Гамма

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

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




Це не дублікат. OP хоче знати, коли розширювати абстрактний клас, а не реалізовувати інтерфейс. Він не хоче знати, коли писати абстрактний клас або інтерфейс. Його абстрактний клас та інтерфейс вже написані. Hd хоче знати, продовжувати чи впроваджувати.
Шиплу Мокаддім

1
@ shiplu.mokadd.im Це відмінність без різниці. Ви не можете використовувати абстрактний клас, не розширюючи його. Твоє вкраплення тут здається абсолютно безглуздим.
Маркіз Лорнський,

Відповіді:


87

Коли використовувати інтерфейси

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

Коли використовувати абстрактні класи

Навпаки, абстрактний клас забезпечує більшу структуру. Зазвичай він визначає деякі реалізації за замовчуванням та надає деякі інструменти, корисні для повної реалізації. Суть полягає в тому, що код, що використовує його, повинен використовувати ваш клас як основу. Це може бути дуже незручно, якщо інші програмісти, які бажають використовувати ваш пакет, вже самостійно розробили власну ієрархію класів. У Java клас може успадковувати лише один базовий клас.

Коли використовувати обидва

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


Я думаю, що ОП хоче знати, коли розширювати абстрактний клас, а не впроваджувати інтерфейс
Шиплу Мокаддім,

@ shiplu.mokadd.im Насправді ОП задало дуже конкретне запитання, на яке відповідь є "так" або "ні".
Маркіз Лорнський,

4
Ти маєш рацію. Але ТАК ми відповідаємо так / ні з належним поясненням.
Шиплу Мокаддім

1
@ shiplu.mokadd.im Я не бачу, як це дає вам ліцензію на неправильне висловлення його питання.
Маркіз Лорнський,

Тільки на підставі цього єдиного твердження 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. Абстрактний клас може визначати як статичні, так і константи екземпляра (final)
  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


10

Тут є багато чудових відповідей, але я часто вважаю, що використання ОБИХ інтерфейсів та абстрактних класів є найкращим шляхом. Розглянемо цей надуманий приклад:

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

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);
    }

але тепер дитина-фахівець з 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


4

За останні три роки ситуація сильно змінилася завдяки доданню нових можливостей інтерфейсу до випуску Java 8.

Зі сторінки документації oracle на інтерфейсі:

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

Як ви цитували у своєму запитанні, абстрактний клас найкраще підходить для шаблону методу шаблону, де вам потрібно створити каркас. Тут не можна використовувати інтерфейс.

Ще одне зауваження - віддавати перевагу абстрактному класу перед інтерфейсом:

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

Абстрактний клас встановлює "це" зв'язок між пов'язаними класами, а інтерфейс надає "можливість" між не пов'язаними класами .


Що стосується другої частини Вашого запитання, яка діє для більшості мов програмування, включаючи java до випуску java-8

Як завжди є компроміс, інтерфейс дає вам свободу щодо базового класу, абстрактний клас дає вам свободу додавати нові методи пізніше. - Еріх Гамма

Ви не можете змінити інтерфейс, не змінюючи багато інших речей у своєму коді

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

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

Щоб вибрати один із них між інтерфейсом та абстрактним класом, на сторінці документації oracle цитується таке:

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

З інтерфейсами всі поля автоматично є загальнодоступними, статичними та остаточними, а всі методи, які ви оголошуєте або визначаєте (як методи за замовчуванням), є загальнодоступними. Крім того, ви можете розширити лише один клас, незалежно від того, абстрактний він чи ні, тоді як ви можете реалізувати будь-яку кількість інтерфейсів.

Для отримання детальної інформації зверніться до цих суміжних питань:

Інтерфейс проти абстрактного класу (загальний ОО)

Як я мав пояснити різницю між класом Interface та Abstract?

Підсумовуючи: зараз баланс більше нахиляється до інтерфейсів .

Чи існують інші сценарії, крім згаданих вище, де конкретно нам потрібно використовувати абстрактний клас (один - див. Це шаблон шаблону методу дизайну концептуально заснований лише на цьому)?

Деякі шаблони дизайну використовують абстрактні класи (над інтерфейсами), крім шаблону методу шаблону.

Творчі моделі:

Анотація_фабрика_шаблон

Структурні структури:

Візерунок_декоратора

Моделі поведінки:

Шаблон посередника


Це: "Абстрактний клас встановлює" - це "зв'язок між пов'язаними класами та інтерфейсом, що надає" має "можливість між не пов'язаними класами".
Габріель

3

Ви не праві. Є багато сценаріїв. Просто неможливо звести його до одного правила із 8 слів.


1
Хіба що ти невиразний, як; Використовуйте інтерфейс, коли тільки можете;)
Пітер Лорі,

@PeterLawrey Так, не дозволяйте круговим аргументам сповільнювати вас ;-)
Маркіз Лорнський,

Зрештою, це "переповнення стека". ;) Я хочу сказати, що якщо ви можете використовувати простіший інтерфейс, зробіть це. В іншому випадку вам не залишається нічого іншого, як використовувати абстрактний клас. Я не бачу це дуже складним.
Пітер Лорі

я думаю, що ви могли б надати більш конструктивну ідею. як розмова про деякі репрезентативні сценарії /
Адамс.H

3

Найкоротша відповідь - розширити абстрактний клас, коли в ньому вже реалізовані деякі функціональні можливості, які ви шукаєте.

Якщо ви реалізуєте інтерфейс, вам доведеться реалізувати весь метод. Але для абстрактного класу кількість методів, які вам потрібно реалізувати, може бути менше.

У шаблоні дизайну шаблону повинна бути визначена поведінка. Така поведінка залежить від інших абстрактних методів. Створюючи підклас та визначаючи ці методи, ви фактично визначаєте основну поведінку. Основна поведінка не може бути в інтерфейсі, оскільки інтерфейс нічого не визначає, він просто оголошує. Тож шаблон шаблону дизайну завжди йде з абстрактним класом. Якщо ви хочете зберегти потік поведінки незмінним, ви повинні розширити абстрактний клас, але не замінювати основну поведінку.


Додаткові посилання на чисту віртуальну функцію додадуть більше інформації про збіжність абстрактного класу та інтерфейсу , 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, яка повинна бути реалізована різними класами реалізаторів. Але інтерфейси страждають від обмеження, що вони не можуть забезпечити реалізацію методів. Це означає, що кожен реалізатор інтерфейсу повинен явно реалізовувати всі методи, заявлені в інтерфейсі, навіть коли деякі з них методи представляють незмінну частину функціональних можливостей і мають абсолютно однакову реалізацію у всіх класах реалізаторів, що призводить до надлишкового коду.Наступний приклад демонструє, як шаблон "Абстрактний батьківський клас" можна використовувати в таких випадках, не вимагаючи надмірних реалізацій методів. "


2

Абстрактні класи відрізняються від інтерфейсів двома важливими аспектами

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

Я б закінчив, що інтерфейси можуть мати змінні, але вони за замовчуванням остаточні.
Томаш Муларчик

1

Це хороше запитання. Два з них не схожі, але їх можна використовувати з тієї ж причини, як перезапис. При створенні найкраще використовувати інтерфейс. Коли справа доходить до класу, це добре для налагодження.


0

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.


0

Це моє розуміння, сподіваюся, це допоможе

Абстрактні класи:

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

Інтерфейси:

  1. Може містити змінні, але всі вони є загальнодоступними статичними кінцевими змінними
    • постійні значення, які ніколи не змінюються зі статичним масштабом
    • нестатичні змінні вимагають екземпляра, і ви не можете створити екземпляр інтерфейсу
  2. Усі методи є абстрактними (в абстрактних методах немає коду)
    • весь код повинен бути фактично написаний у класі, який реалізує конкретний інтерфейс

0

Використання реферату та інтерфейсу:

Один має "Є-А-відносини", а інший "має-А-відносини"

Властивості за замовчуванням встановлені абстрактно, а додаткові властивості можуть бути виражені через інтерфейс.

Приклад: -> У людини ми маємо деякі властивості за замовчуванням, такі як їжа, сон тощо, але якщо хтось має будь-які інші навчальні заходи, такі як плавання, гра тощо, це може бути виражено за допомогою Interface.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.