Чому я повинен оголошувати клас абстрактним класом?


40

Я знаю синтаксис, правила, застосовані до абстрактного класу, і хочу знати використання абстрактного класу

Абстрактний клас не може бути безпосередньо встановлений, але може бути розширений іншим класом

У чому перевага цього?

Чим він відрізняється від інтерфейсу?

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

Мені відомо про використання інтерфейсу. Я дізнався, що з моделі делегування подій моделі AWT на Java.

У яких ситуаціях я повинен оголосити клас абстрактним класом? Яка користь від цього?


14
Це запитали, ви знаєте. У результаті пошуку з’являться інші подібні питання. Слід почати з Google, що призведе до переповнення стека. Все це - дублікати: stackoverflow.com/search?q=ab Abstract+interface .
S.Lott

1
"використання класу" Анотація "вони просто обговорюють ... правила". Що? Що відрізняється між "правилами" та "використанням"? До речі, ваше запитання було задано. Я знаю. Я відповів на це. Продовжуй дивитись. Важливо навчитися використовувати пошук.
S.Lott

Я маю на увазі "У яких ситуаціях я повинен оголосити клас абстрактним класом? В чому переваги цього?"
Вайбхав Яні

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

Я вважаю, що шаблон шаблону методу є дуже потужним і хорошим випадком використання для абстрактних класів.
m3th0dman

Відповіді:


49

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

З чисто технічної точки зору, ніколи не існує вимоги оголошувати клас абстрактним.

Розглянемо наступні три класи:

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

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

Незважаючи на це , ви все одно отримаєте поліморфізм, так до тих пір , як ваша програма тільки робить SqlDatabaseі OracleDatabaseекземпляри, ви могли б написати такі методи , як:

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

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

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

Розглянемо наступний метод:

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

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

Іноді поєднання обох працює дуже добре. Наприклад:

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

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


16

Подібність

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

Різниця

  1. Інтерфейси

    • Визначте добре відомий державний контракт, здібності типу
    • Застосовується для показу горизонтального успадкування, тобто розгалуження на першому рівні спадкування (наприклад, ILog для визначення засобів реєстрації даних до бази даних, текстового файлу, XML, SOAP тощо)
    • Усі члени є публічними
    • Не дозволяється реалізація
    • Спадковій дитині може бути багато інтерфейсів для реалізації
    • Корисно для інтеграції третіх сторін
    • Найменування зазвичай починається з I
  2. Анотація класу

    • Визначте структуру, особистість та деяку поведінку, що підтримується за замовчуванням
    • Застосовується для показу вертикального успадкування, тобто глибокого розгалуження на декількох рівнях (наприклад, клас ClassEEnceity в розробці, керованій доменом)
    • Учасники можуть мати різну видимість (від публічної до приватної)
    • Ви можете реалізувати деякі члени (наприклад, * Читання читачів)
    • У спадщині дитина може мати лише один базовий абстрактний клас

Адже насправді легко знайти відповідь простим запитом google .


що ти мені за реалізацією не дозволено? java-клас може реалізовувати інтерфейси.
Саджук

@Sajuuk ця лінія відноситься до інтерфейсу. Ви не можете помістити реалізацію контракту в інтерфейс. У вас може бути реалізація контракту за замовчуванням у абстрактному класі.
олексій

11

Чим він відрізняється від інтерфейсу?

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


Це дійсно потребує оновлення Java 8, де були введені методи за замовчуванням.
Haakon Løtveit

8

Абстрактні класи для "є" відносини, а інтерфейси - для "вмію робити".

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


3

Крім глибоких технічних деталей - як реалізація деяких методів для абстрактних класів тощо, сенс такий:

Інтерфейси визначають загальну можливість - IEnumerable визначає, що клас, який реалізує цей інтерфейс, можна перерахувати. Це нічого не говорить про сам клас.

Абстрактні (або базові) класи визначають поведінку - WebRequest визначає загальну поведінку всіх дочірніх класів, таких як HttpWebRequest тощо. Він визначає основне значення класу та його реальна мета - доступ до веб-ресурсів.


2

Запис у Вікіпедії .

Основна відмінність інтерфейсу від абстрактного класу полягає в тому, що абстрактний клас може надавати реалізовані методи. За допомогою інтерфейсів можна лише оголошувати методи, писати їх підпис. Ось приклад класу, який розширює абстрактний клас, який реалізує два інтерфейси: (java)

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

У цьому прикладі MyAbstractClass надає відкритий метод , який друкує всі три значення. В ImpClass потрібно реалізувати getValue1, а getValue2 відповідно з MyInterface1 та MyInterface2 та getValue3 з абстрактного класу.

Voilà.

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

Зрештою, абстрактний клас, який надає лише абстрактні методи, - це "чистий" абстрактний базовий клас, який також називається інтерфейсом.


2
  • Інтерфейс - коли кілька класів діляться API (назви методів та параметри)
  • Абстрактний клас - коли кілька класів поділяють один і той же код (реалізація)

Іншими словами, ви повинні почати з питання: "Чи обов'язково такі класи поділяють реалізацію , чи вони просто мають спільний інтерфейс ?"

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

Існують також інші способи спільного використання реалізації, наприклад, інкапсуляція об'єкта з цією реалізацією (наприклад, у шаблоні стратегії ).


1

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

Наприклад, розглянемо гру, де є різні типи ігрових утворень. Всі вони успадковують від базового GameEntityкласу.

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

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

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

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

Повернемося до гри як приклад. Розглянемо клас, з Enemyякого походить GameEntity. У цьому класі є метод attackMeFromDistance(RangedAttacker attacker). Цей метод призначений для того, щоб дозволити суб'єктам атакувати ворога здалеку.

Як бачите, цей метод приймає RangedAttackerтип як параметр. Однак усі ігрові суб'єкти вже успадковують GameEntity. Вони не можуть продовжити інший клас.

Візьміть заняття Mageі, Archerнаприклад. Ми хочемо дозволити, щоб обидва вони були прийняті як параметри attackMeFromDistance(RangedAttacker attacker)методу, але вони вже походять від GameEntity.

Для вирішення цього питання ми створюємо новий інтерфейс:

interface RangedAttacker{
    public void attackFromDistance();
}

Клас, який реалізує цей інтерфейс, повинен реалізувати attackFromDistance()метод, і таким чином гарантується, що він має діапазон можливостей атакуючих. Це означає, що attackMeFromDistanceметод тепер може безпечно приймати класи, які реалізують цей інтерфейс. Тому створення Mageта Archerреалізація цього інтерфейсу вирішує нашу проблему.

Для мене це сила інтерфейсів.

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


0
  1. Є ймовірність, що занадто мало методів є загальними для деяких класів (бізнес-логіка). А інші методи різні. У таких сценаріях ви можете реалізувати всі загальні методи в одному класі, а решту оголосити абстрактними. Тоді слід оголосити клас абстрактним.
  2. Іноді ви не дозволяєте створювати об'єкт безпосередньо класу. Такого класу, що потрібно оголосити клас абстрактним.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.