Коли використовувати: метод за замовчуванням інтерфейсу Java 8+ порівняно з абстрактним методом


540

Java 8 дозволяє реалізувати за замовчуванням методи в інтерфейсах, званих Методами за замовчуванням .

Я плутаюсь між тим, коли б я використовував такий вид interface default methodзамість abstract classabstract method(s)).

Тож коли слід використовувати інтерфейс із методами за замовчуванням та коли слід використовувати абстрактний клас (із абстрактними методами)? Чи корисні для цього сценарію абстрактні класи?


38
Можливо, ви все ще не можете мати поля, приватні методи тощо в інтерфейсах, тоді як ви можете в абстрактному класі?
sp00m

2
Мені раніше було цікаво про цю тему, тепер я зрозумів. Дякую @Narendra Pathai. Я хотів би додати посилання на іншу тему, яку ви запитували щодо тієї ж теми, оскільки обидва мої сумніви були. stackoverflow.com/questions/19998309/…
Ашутош Ранджан

Ви можете знайти приємне повідомлення про це тут: blog.codefx.org/java/everything-about-default-methods
Fabri Pautasso

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

@DaBlick Не могли б ви вирішити проблему стану в інтерфейсі через HashMap. Наприклад: якщо ви хочете клас Foo, який містить int a, b, String c. і ви хочете, щоб вони мали стан, створіть HashMap </ * ім'я Foo * * / String, / * карта полів * / Hashmap </ * назва конкретного поля * / String, / * значення поля * / Об'єкт >> карта . Коли ви хочете "інстанціювати" теоретичний клас Foo, у вас є метод, instantiate (String nameOfFoo), який робить map.put (nameOfFoo, поля), де полями є HashMap <String, Object> polja.put ("a", new int ("5")); field.put ("b", новий int ("6")); field.put ("c", "blah"));
Джордж Ксав'є

Відповіді:


307

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

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

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

Початковою мотивацією впроваджувати defaultметоди в Java 8 було бажання розширити інтерфейси Collections Framework ламбда-орієнтованими методами, не порушуючи жодних існуючих реалізацій. Хоча це більше стосується авторів публічних бібліотек, ви можете виявити ту саму функцію корисною і у вашому проекті. У вас є одне централізоване місце, де можна додати нові зручності, і вам не потрібно покладатися на те, як виглядає решта ієрархії типів.


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

3
Єдине використання абстрактних класів в епоху Java 8, яку я бачу, - це визначення нефінальних полів. В інтерфейсах поля за замовчуванням є остаточними, тому ви не можете їх змінити, як тільки вони будуть призначені.
Anuroop

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

2
@PhilipRego Абстрактні методи нічого не викликають, оскільки вони не мають реалізації. Реалізовані методи в класі можуть отримати доступ до стану класу (змінні екземпляри). Інтерфейси не можуть оголосити їх, тому методи за замовчуванням не можуть отримати доступ до них. Вони повинні покладатися на клас, що забезпечує реалізований метод, який отримує доступ до стану.
Марко Тополник

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

125

Існує кілька технічних відмінностей. Абстрактні класи все ще можуть зробити більше порівняно з інтерфейсами Java 8:

  1. Абстрактний клас може мати конструктор.
  2. Абстрактні заняття більш структуровані і можуть містити стан.

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


20
Ця відповідь насправді правильна і має сенс особливо "Концептуально, головне призначення методів захисника - це зворотна сумісність"
Проба

1
@UnK known Ця сторінка дає більше уявлень
bernie

@UnK known, в основному це дозволяє додавати методи в інтерфейс і класи, які реалізують цей інтерфейс, автоматично отримують цю функціональність.
LegendLength

3
Більш тонкий пункт про точку немає. 2 вище про "може утримувати стан це". Абстрактні класи можуть містити стан, який можна змінити пізніше. Інтерфейси можуть також містити стан, але як тільки стан призначається після створення екземпляра, стан не може бути змінено.
Anuroop

3
@Anuroop Я б не описав public static finalполя інтерфейсу як "стан". У staticчастині означає , що the're не відносяться до конкретного екземпляру на всіх. Вони призначаються при створенні екземплярів класу , що не те саме, що після створення екземпляра .
geronimo

65

Це описано в цій статті . Подумайте про forEachколекції.

List<?> list = 
list.forEach(…);

Для forEach ще не оголошено інтерфейсом, java.util.Listані java.util.Collectionінтерфейсом. Одним із очевидних рішень було б просто додати новий метод до наявного інтерфейсу та забезпечити реалізацію, де це потрібно в JDK. Однак після опублікування неможливо додати методи в інтерфейс, не порушивши існуючої реалізації.

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


1
"неможливо додати методи в інтерфейс, не порушуючи існуючої реалізації" - це?
Андрій Чащев

26
@AndreyChaschev Якщо ви додасте новий метод в інтерфейс, то всі реалізатори повинні реалізувати цей новий метод. Тому він порушує існуючі реалізації.
Марко Топольник

4
@MarkoTopolnik спасибі, пропустив це. Зазначимо, є спосіб частково уникнути цього - шляхом подання цього методу в абстрактній реалізації за замовчуванням. У цьому прикладі це було б AbstractList::forEachкидання UnsupportedOperationException.
Андрій Чащев

3
@AndreyChaschev Так, це було по-старому (км ... це теперішній шлях :), з дефіцитом, який обмежує виконавця до одного успадкування від наданої абстрактної реалізації.
Марко Топольник

Я не зламаю, якщо так трапиться, що раніше, всі реалізації включали цей метод. Що малоймовірно, але можливо.
Джордж Ксав'є

19

Ці два досить різні:

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

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


18

Як описано в цій статті,

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

Після введення методу за замовчуванням здається, що інтерфейси та абстрактні класи однакові. Однак вони все ще відрізняються концепцією в Java 8.

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


Я вважаю, що клас Abstrakt має конструктор, який можна визначити на відміну від інтерфейсу. У Java 8 вони також обидва відрізняються один від одного завдяки цьому.
Hemanth Peela

1
Чому абстрактний клас має конструктор, якщо його неможливо встановити?
Джордж Ксав'є

Ми можемо викликати super () з дочірнього класу, який буде викликати конструктор абстрактного класу. Це впливає на стан абстрактного класу.
Sujay Mohan

14

Щодо вашого запиту

Тож коли слід використовувати інтерфейс із методами за замовчуванням та коли слід використовувати абстрактний клас? Чи корисні для цього сценарію абстрактні класи?

java документація забезпечує ідеальну відповідь.

Анотаційні класи порівняно з інтерфейсами:

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

Однак за допомогою абстрактних класів можна оголосити поля, які не є статичними та остаточними, та визначити публічні, захищені та приватні конкретні методи.

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

Випадки використання для кожного з них були пояснені нижче в публікації SE:

Чим відрізняється інтерфейс від абстрактного класу?

Чи корисні для цього сценарію абстрактні класи?

Так. Вони все ще корисні. Вони можуть містити нестатичні, не остаточні методи та атрибути ( захищені, приватні додатково до загальнодоступних ), що неможливо навіть із інтерфейсами Java-8.


Тепер інтерфейси мають і приватні методи howtodoinjava.com/java9/java9-private-interface-methods
valijon

13

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

  1. Методи за замовчуванням поклали край класичному шаблону інтерфейсу та супутньому класу, який реалізує більшість або всі методи в цьому інтерфейсі. Прикладом є Collection and AbstractCollection. Тепер нам слід реалізувати методи в самому інтерфейсі для забезпечення функціональності за замовчуванням. Класи, які реалізують інтерфейс, мають можливість змінити методи або успадкувати реалізацію за замовчуванням.
  2. Ще одне важливе використання методів за замовчуванням interface evolution. Припустимо, я мав клас балу як:

    public class Ball implements Collection { ... }

Тепер у Java 8 представлена ​​нова функція потоків. Ми можемо отримати потік, використовуючи streamметод, доданий до інтерфейсу. Якби streamне метод за замовчуванням, всі реалізації Collectionінтерфейсу були б зламані, оскільки вони не реалізували б цей новий метод. Додавання методу, що не використовується за замовчуванням, до інтерфейсу - це не так source-compatible.

Але припустимо, що ми не перекомпілюємо клас та використовуємо старий файл jar, який містить цей клас Ball. Клас буде завантажуватись добре без цього відсутнього методу, екземпляри можна створити, і, здається, все працює нормально. АЛЕ якщо програма викличе streamметод на примірник, Ballми отримаємо AbstractMethodError. Тож прийняття методу за замовчуванням вирішило обидві проблеми.


9

Методи за замовчуванням в інтерфейсі Java дозволяють розвивати інтерфейс .

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

Статичний метод унікальний для класу. Метод за замовчуванням унікальний для екземпляра класу.

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

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

Більше про тему тут .


7

Хоча це старе питання, я також можу дати свій внесок на це.

  1. абстрактний клас: Всередині абстрактного класу ми можемо оголосити змінні екземпляра, необхідні дочірньому класу

    Інтерфейс: Всередині змінного інтерфейсу всі змінні завжди є загальнодоступними статичними і остаточними, ми не можемо оголосити змінні екземпляра

  2. абстрактний клас: Абстрактний клас може говорити про стан об'єкта

    Інтерфейс: Інтерфейс ніколи не може говорити про стан об'єкта

  3. абстрактний клас: Всередині Абстрактний клас ми можемо оголосити конструктори

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

  4. абстрактний клас: Усередині абстрактного класу ми можемо оголосити блоки екземплярів та статики

    Інтерфейс: Інтерфейси не можуть мати блоки примірника та статику.

  5. абстрактний клас: Абстрактний клас не може посилатися на лямбда-вираз

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

  6. абстрактний клас : Всередині абстрактного класу ми можемо замінити методи ОБ'ЄКТНОГО КЛАСУ

    Інтерфейси: Ми не можемо замінити методи OBJECT CLASS всередині інтерфейсів.

Закінчу на замітці, що:

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


5

Правило Ремі Форакс - Ви не розробляєте класи з абстрактними класами. Ви розробляєте додаток за допомогою інтерфейсів . Watever - це версія Java, якою б не була мова. Вона спирається на I принцип сегрегації nterface в SOL I D принципів.

Пізніше ви можете використовувати абстрактні класи для факторизації коду. Тепер з Java 8 ви можете це зробити безпосередньо в інтерфейсі. Це заклад, не більше.


2

коли слід використовувати інтерфейс із методами за замовчуванням та коли слід використовувати абстрактний клас?

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

Факти та обмеження:

1 травня оголошується лише в інтерфейсі, а не в класі або абстрактному класі.

2-Повинен забезпечити тіло

3-Не вважається абстрактним, як інші звичайні методи, що використовуються в інтерфейсі.


1

У Java 8 інтерфейс виглядає як абстрактний клас, хоча їх можуть бути деякі відмінності, такі як:

1) Абстрактні класи - це класи, тому вони не обмежуються іншими обмеженнями інтерфейсу на Java, наприклад абстрактний клас може мати стан , але ви не можете мати стан інтерфейсу на Java.

2) Ще одна смислова різниця між інтерфейсом з методами за замовчуванням та абстрактним класом полягає в тому, що ви можете визначити конструктори всередині абстрактного класу , але ви не можете визначити конструктор всередині інтерфейсу на Java


Я погоджуюся з №2, але для №1, ви не можете просто реалізувати інтерфейс і таким чином мати стан через клас реалізації?
Джордж Ксав'є

0

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

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


0

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

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

Більш конкретно, Java дозволяє лише одинакове успадкування реалізації, але вона дозволяє багатократно успадковувати інтерфейси. Наприклад, дійсний код Java:

class MyObject extends String implements Runnable, Comparable { ... }

MyObject успадковує лише одну реалізацію, але вона успадковує три договори.

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

На підтвердження моєї точки зору, ось цитата Кена Арнольда та Джеймса Гослінга з книги "Мова програмування Java", 4-е видання :

Однозначне успадкування виключає деякі корисні та правильні конструкції. Проблеми багаторазового успадкування виникають внаслідок багаторазового успадкування реалізації, але у багатьох випадках множинне спадкування використовується для успадкування ряду абстрактних контрактів і, можливо, однієї конкретної реалізації. Надання засобів для успадкування абстрактного контракту без успадкування реалізації дозволяє набрати переваги від багаторазового успадкування без проблем багаторазового успадкування. Успадкування абстрактного договору називається спадкоємністю інтерфейсу . Мова програмування Java підтримує успадкування інтерфейсу, дозволяючи оголосити interfaceтип


-1

Спочатку подумайте про принцип відкритого / закритого типу. Методи за замовчуванням в інтерфейсах НЕ ПОРУШУЮТЬ це. Це погана особливість у Java. Це заохочує поганий дизайн, погану архітектуру, низьку якість програмного забезпечення. Я б запропонував уникати використання методів за замовчуванням повністю.

Задайте собі кілька питань: Чому ви не можете розмістити свої методи до абстрактного класу? Вам знадобиться більше одного абстрактного класу? Тоді подумайте, за що відповідає ваш клас. Ви впевнені, що всі методи, які ви збираєтеся застосувати до одного класу, дійсно виконують одне й те саме? Можливо, ви виділите кілька цілей, а потім розділите свій клас на кілька класів, для кожної мети - власний клас.

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