Почему я должен объявлять класс как абстрактный класс?


40

Я знаю синтаксис, правила, применяемые к абстрактному классу, и я хочу знать использование абстрактного класса

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

В чем преимущество этого?

Чем он отличается от интерфейса?

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

Я осведомлен об использовании интерфейса. Я узнал об этом из модели делегирования событий AWT на Java.

В каких ситуациях я должен объявлять класс как абстрактный класс? Каковы преимущества этого?


14
Это спросили, вы знаете. Поиск вызовет другие вопросы, как этот. Вы должны начать в Google, что приведет вас к переполнению стека. Все они являются дубликатами: stackoverflow.com/search?q=abstract+interface .
С.Лотт

1
msgstr "использование класса Abstract они просто обсуждают ... правила". Какая? Чем отличаются «правила» и «использование»? Кстати, ваш точный вопрос был задан. Я знаю. Я ответил на это. Продолжай смотреть. Важно научиться пользоваться поиском.
С.Лотт

Я имею в виду «В каких ситуациях я должен объявлять класс абстрактным классом? В чем его преимущества?»
Вайбхав Яни

Интерфейс - это чистый абстрактный класс, вот и все. Они один в одном. Я все время использую абстрактный класс для функции в базовом классе, которая не может быть реализована, потому что ей нужны данные, которые есть только у подклассов, но я хочу убедиться, что каждый подкласс имеет эту функцию и реализует ее соответствующим образом.
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

Вам не нужно делать класс Database абстрактным, даже если есть очевидная проблема с его реализацией: когда вы пишете эту программу, вы можете печатать, 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. Абстрактный класс

    • Определить структуру, идентичность и некоторые поддерживаемые по умолчанию поведение
    • Применимо для демонстрации вертикального наследования, то есть глубокого ветвления на нескольких уровнях (например, класс AbstractEntity в разработке, управляемой доменом)
    • Участники могут иметь различную видимость (от общедоступной до частной)
    • Вы можете реализовать некоторые члены (например, * классы Reader)
    • Наследование потомка может иметь только один базовый абстрактный класс

Это действительно легко найти ответ с помощью простого запроса Google .


какие у вас мены при реализации не допускаются? Java-класс может реализовывать интерфейсы.
Саджук

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

11

Чем он отличается от интерфейса?

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


Это действительно нуждается в обновлении для Java 8, где были представлены методы по умолчанию.
Хакон Лотвейт

8

Абстрактные классы предназначены для отношений "is a", а интерфейсы - для "can do".

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


3

Помимо глубоких технических деталей - таких как реализация некоторых методов для абстрактных классов и т. Д., Смысл такой:

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

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


2

Wikipedia запись .

Основное различие между интерфейсом и абстрактным классом состоит в том, что абстрактный класс может предоставлять реализованные методы. С интерфейсами вы можете только объявить методы, написать их подпись. Вот пример класса, который расширяет абстрактный класс, который реализует два интерфейса: (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 из абстрактного класса.

Вуаля.

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

В заключение отметим, что абстрактный класс, который предоставляет только абстрактные методы, является «чистым» абстрактным базовым классом, или интерфейсом.


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.