Может ли быть конструктор у абстрактного класса для чего

Обновлено: 17.05.2024

Если да, то как его можно использовать и для каких целей?

да, абстрактный класс может иметь конструктор. Рассматривайте это:

суперкласс Product является абстрактным и имеет конструктор. Конкретный класс TimesTwo имеет конструктор, который просто жестко кодирует значение 2. Конкретный класс TimesWhat имеет конструктор, который позволяет звонящему указать значение.

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

Примечание: поскольку в Родительском конструкторе нет конструктора по умолчанию (или no-arg абстрактный класс, конструктор, используемый в подклассе, должен явно вызывать родительский конструктор.

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

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

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

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

да! абстрактные классы могут иметь конструкторы!

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

когда мы создаем объект любого подкласса все конструкторы в соответствующем дереве наследования вызываются в подход сверху вниз. То же самое относится и к абстрактным классам. Хотя мы не можем создать объект абстрактного класса, когда мы создаем объект класса, который является конкретным и подкласс абстрактного класса, конструктор абстрактного класса вызывается автоматически. Следовательно, мы можем иметь конструктор в абстрактных классах.

Примечание: не-абстрактный класс не может иметь абстрактные методы, но абстрактный класс может иметь не-абстрактный метод. Причина аналогична конструкторы, разница в том, что вместо автоматического вызова мы можем вызвать super (). Кроме того, нет ничего похожего на абстрактный конструктор, поскольку он вообще не имеет смысла.

не только может, это всегда так. Если вы не укажете один, то у него нет конструктора arg по умолчанию, как и у любого другого класса. Фактически, все классы, включая вложенные и анонимные классы, получат конструктор по умолчанию, если он не указан (в случае анонимных классов его невозможно указать, поэтому вы всегда получите конструктор по умолчанию).

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

Да, это может, абстрактные классы конструкторы обычно используются для супер-вызовов событий инициализации, общих для всех подклассов

абстрактный класс может иметь конструктор, но вы не можете создать объект абстрактного класса, так как вы можете использовать этот конструктор?

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

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

конструктор НЕ СОЗДАЕТ ОБЪЕКТ. Он используется для инициализации объекта.

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

абстрактный класс (В отличие от интерфейса) могут иметь non-final нестатические поля, которые нуждаются в инициализации. Для этого вы можете написать свой собственный конструктор в абстрактном классе. Но в этом случае не будет никакого конструктора по умолчанию.

будьте осторожны при расширении выше абстрактного класса, вы должны явно вызвать super из каждого конструктора. Первая строка любого конструктора вызывает super (). если вы явно не вызываете super (), Java сделает это за вас. Ниже код не будет компилироваться:

у вас есть чтобы использовать его, как показано ниже пример:

рассмотрим следующий пример:

суперкласс является абстрактным и имеет конструктор.

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

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

в конкретном классе объявление конструктора для конкретного типа Fnord эффективно предоставляет две вещи:

средство, с помощью которого код может запросить создание экземпляра Fnord

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

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

1. Могут ли в языке Java у абстрактного класса быть конструкторы?

Да, в абстрактном классе в Java можно объявить и определить конструкторы. Поскольку создавать экземпляры абстрактных классов нельзя, вызвать такой конструктор можно только при формировании цепочки конструкторов, то есть при создании экземпляра конкретного класса-реализации. Но представьте, что интервьюер задаст затем вопрос: а какой смысл в конструкторе, если создать экземпляр абстрактного класса все равно нельзя? Дело в том, что его всё равно можно использовать для задания начальных значений общих переменных, объявленных в абстрактном классе и используемых различными реализациями. Даже если вы не объявили никакого конструктора, компилятор добавит в абстрактный класс конструктор по умолчанию без аргументов. Без него ваш подкласс не скомпилируется, поскольку первый оператор в любом конструкторе представляет собой неявный вызов super() – конструктора суперкласса по умолчанию в языке Java.

2. Могут ли абстрактные классы в языке Java реализовывать интерфейсы? Должны ли они реализовывать все методы?

Да, абстрактные классы могут реализовывать интерфейсы с помощью ключевого слова implements . Поскольку они абстрактные, то не обязаны реализовывать все методы. Наличие абстрактного базового класса и интерфейса для объявления типа является рекомендуемой практикой. Пример — интерфейс java.util.List и соответствующий абстрактный класс java.util.AbstractList . Поскольку AbstractList реализует все общие методы, то конкретные реализации (например, LinkedList и ArrayList ) не должны реализовать все методы, как в случае, если бы они реализовали интерфейс List напрямую. Это решение сочетает преимущество использования интерфейса для объявления типа и гибкость абстрактного класса для реализации всего общего поведения в одном месте. В книге Джошуа Блоха «Java. Эффективное программирование» есть отличная глава на тему использования интерфейсов и абстрактных классов в Java, для лучшего понимания имеет смысл её изучить.

3. Может ли абстрактный класс быть final?

Нет, не может. Ключевое слово final означает, что класс на вершине иерархии, и у него не может быть наследников. А абстрактный класс без наследников — это сферический конь в вакууме, так как нельзя создать экземпляр abstract class . Таким образом, если класс одновременно abstract и final , то у него нет наследников и нельзя создать его экземпляр. Компилятор Java выдаст ошибку, если сделать класс одновременно abstract и final .

4. Могут ли у абстрактного класса в языке Java быть статические методы?

Да, абстрактные классы могут объявлять и определять статические методы. Только необходимо следовать общим принципам создания статических методов в Java, поскольку они нежелательны при объектно-ориентированном проектировании, ведь переопределение статических методов в Java невозможно. Статические методы в абстрактном классе – явление очень редкое, но, если на это есть уважительные причины, вам ничего не помешает их использовать.

5. Можно ли создать экземпляр абстрактного класса?

Нет, этого делать нельзя. Суть абстрактного класса заключается в том, что он не завершён, и его нужно завершить в классах-наследниках. То есть этот класс не готов к использованию. В нём, например, может отсутствовать реализация каких-то методов. Раз класс не готов к использованию, то нельзя создавать его объект. А вот экземпляры наследников абстрактного класса создавать можно. Компилятор Java выдаст ошибку, если программа попытается создать экземпляр абстрактного класса.

6. Обязательно ли в абстрактном классе должны быть абстрактные методы?

Нет, в абстрактном классе может не быть ни одного абстрактного метода. Сделать класс абстрактным в языке Java можно просто путем использования ключевого слова abstract при объявлении. Компилятор обеспечит выполнение всех структурных ограничений, например, запрета на создание экземпляров этого класса. Кстати, вопрос о том, должны ли быть абстрактные методы в абстрактном классе или интерфейсе – спорный. Мне представляется, что в абстрактном классе должны быть абстрактные методы, поскольку это первое, о чем думает программист, видя абстрактный класс. Это хорошо согласуется с принципом минимизации неожиданностей.

7. Каковы различия между абстрактным классом и интерфейсом в Java?

  • Интерфейс описывает только поведение (методы) объекта, а вот состояний (полей) у него нет (кроме public static final ), в то время как у абстрактного класса они могут быть.
  • Абстрактный класс наследуется (extends), а интерфейс — реализуется (implements). Мы можем наследовать только один класс, а реализовать интерфейсов — сколько угодно. Интерфейс может наследовать (extends) другой интерфейс/интерфейсы.
  • Абстрактные классы используются, когда есть отношение "is-a", то есть класс-наследник расширяет базовый абстрактный класс, а интерфейсы могут быть реализованы разными классами, вовсе не связанными друг с другом.

8. Когда имеет смысл предпочесть абстрактный класс интерфейсу и наоборот?

  • Вы хотите поделиться кодом между несколькими тесно связанными классами.
  • Вы ожидаете, что классы, которые расширяют ваш абстрактный класс, имеют много общих методов или полей, или требуют других модификаторов доступа, кроме public (например, protected и private ).
  • Вы хотите объявить нестатические или не-final поля. Это позволяет вам определять методы, которые могут получить доступ и изменить состояние объекта, которому они принадлежат.
  • Вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, интерфейсы Comparable и Cloneable реализуются многими несвязанными классами.
  • Вы хотите определить поведение конкретного типа данных, но вам не важно, кто его реализует.
  • Вы хотите использовать множественное наследование типа.

9. Что такое абстрактный метод в языке Java?

Абстрактный метод – это метод без тела. Вы просто объявляете метод, не определяя его, с использованием ключевого слова abstract в объявлении метода. Все объявленные внутри интерфейса в языке Java методы – по умолчанию абстрактные. Вот пример абстрактного метода в языке Java: Теперь, для реализации этого метода необходимо расширить абстрактный класс и этот метод переопределить.

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

При этом в абстрактном классе прописан конструктор принимающий данные для инициализации всех переменных класса. Конструкторы с такой же сигнатурой есть и в наследниках, где они попросту перенаправляют свои входящие данные на конструктор super(. ) .

Так вот, все работает хорошо, вот только есть сомнение, что как-то не по фен-шую эти конструкторы сделаны как оптимизировать?

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

А что вы подразумеваете под оптимизацией? Что именно, по вашему мнению, нуждается в улучшении? Какой параметр?

@andreycha Спасибо за ответ! Выбор сделан в пользу абстрактного класса, а не интерфейса, чтобы не дублировать в наследниках N строк с объявлением полей, их геттерами и сеттерами. Классы полностью идентичны по своей сути, разница только в логике ключевого метода

2 ответа 2

По фэн шую, конструктор абстрактного класса должен быть protected , то есть:

На сколько я понял, у вас примерно следующая ситуация:

Основная причина для создания новых классов - разное поведение метода. Если вы вынесете этот метод в функциональный интерфейс, то все равно вам придется объявлять два класса с транзитными конструкторами (которые вызывают super).

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

Таким образом, если идти по пути наследования, то вариант только один - инициализировать параметры через методы уже после создания объекта, т.е. не в конструкторе. Это можно делать как вручную, так и использовать какой-нибудь паттерн.

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

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

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

Почему можно объявить указатель на абстрактный класс, а создать экземпляр абстрактного класса нельзя?(понимаю,что виртуальный класс является , по сути, формой(интерфейсом) всех классов порождённых им => смысла объявлять простой абстрактный класс нет, но не понятна ситуация с указателем).

Есть ли смысл вообще писать конструктор в абстрактном классе? ( по личному мнению - нет, но в примерах абстрактных классов конструкторы встречал ).

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

3 ответа 3

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

В данном примере собственно основной "алгоритм" реализуется функцией execute() , которой нет никаких причин быть виртуальной. Все разнообразие поведений разных "программ" реализуется классами-наследниками через перекрытие функций init() , run() и done() , а сам "алгоритм" остается неперекрываемым.

Поведение виртуальных функций в языке С++ фундаментально основано на понятии динамического типа объекта. Виртуальный вызов функции - это по определению вызов, при котором конкретная версия функции выбирается на основе анализа динамического типа объекта, использованного в этом вызове. По этой причине виртуальный вызов возможен только тогда, когда динамический тип объекта уже определен (проинициализирован, сформирован, зафиксирован - выберете тот термин, который вам нравится). Так вот конструктор - это именно та специальная функция, которая формирует, инициализирует тип объекта. До того момента, как конструктор отработал, объекта еще не существует и тип объекта еще не определен. Т.е. понятие виртуального вызова к такому "сырому" объекту еще не применимо. По этой причине в С++ не может быть виртуальных конструкторов.

Другими словами, в С++ конструктор - эта та самая функция, которая инициализирует механизм виртуальных вызовов. Поэтому сам конструктор вызвать виртуально невозможно - на этот момент функциональность виртуальных вызовов еще не проинициализирована.

Физически, в повсеместно используемой реализации полиморфизма, функциональность виртуальных функций обеспечивается так называемой таблицей виртуальных функций, указатель на которую хранится в каждом полиморфном объекте. Так вот конструктор, кроме прочего, как раз таки и занимается привязкой каждого полиморфного объекта к его правильной таблице виртуальных функций. До того, как такая привязка выполнена, виртуальность работать не может.

Хм. Совершенно верно у каждого класса - свой деструктор. Но виртуальность нужна именно для того, чтобы при полиморфном удалении объекта был правильно выбран именно правильный деструктор, в соответствии с динамическим типом удаляемого объекта. Т.е. если у вас есть указатель на базовый класс Base *p , который на самом деле указывает на объект наследного типа Derived

то при выполнении delete p (полиморфное удаление) вам нужно, чтобы был вызван именно деструктор класса Derived . Чтобы это произошло, деструктор должен быть виртуальным.

Если деструктор в такой ситуации невиртуален, то, как правильно заметил @VladD в комментариях, поведение программы не определено.

Создать самостоятельный экземпляр абстрактного класса нельзя потому, что спецификация языка это запрещает. Да и какой смысл в этом создании? Абстрактный класс - это класс с "несуществующими" (ненаписанными) виртуальными функциями. Зачем и кому могут понадобиться объекты такого неполноценного класса? Что вы предлагаете делать, если пользователь попробует вызвать такую несуществующую виртуальную функцию? Вызывать неопределенное поведение? Авторы языка решили, что разумнее просто запретить создание самостоятельных экземпляров абстрактных классов.

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

Тут еще можно добавить, что таки есть лазейка, позволяющая попытаться выполнить вызов несуществующей виртуальной функции абстрактного класса: это вызов виртуальной функции из конструктора (или деструктора) абстрактного класса. Чтобы "обмануть" компилятор такой вызов обычно приходится делать через промежуточную функцию

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

Не понимаю, почему возникает такой вопрос. Смысл писать конструктор в абстрактном классе, разумеется, есть, если для такого конструктора в этом классе есть работа. Конструктор должен что-то инициализировать. У вас есть что инициализировать в вашем абстрактном классе? Если есть - то пишите конструктор.

Другое дело, что обычно в абстрактных класса инициализировать нечего. Соответственно и конструктор чаще всего не нужен.

Не понимаю сути вопроса. Дружественность не имеет никакого отношения к виртуальности. Поэтому объявлять виртуальную функцию другом имеет ровно тот же смысл, что и объявлять любую другую (невиртуальную) функцию другом.

Тут стоит еще раз повторить, что дружественность ничего не знает о виртуальности (а виртуальность - о дружественности). Дружественность не распространяется по иерархии классов, т.е. объявленная вами дружественность никак не будет распространяться на "ту же самую" виртуальную функцию в классах-наследниках.

You can now choose to sort by Trending, which boosts votes that have happened recently, helping to surface more up-to-date answers.

Trending is based off of the highest score sort and falls back to it if no posts are trending.

Yes, an abstract class can have a constructor. Consider this:

The superclass Product is abstract and has a constructor. The concrete class TimesTwo has a constructor that just hardcodes the value 2. The concrete class TimesWhat has a constructor that allows the caller to specify the value.

Abstract constructors will frequently be used to enforce class constraints or invariants such as the minimum fields required to setup the class.

NOTE: As there is no default (or no-arg) constructor in the parent abstract class, the constructor used in subclass must explicitly call the parent constructor.

@Jonathon: No real benefit is gained by adding complexity for the purposes of the answering the question asked. If the question was about scope, then, yes, it would make sense to contrast the three useful possibilities.

I think all Jonathon was trying to say is that a public constructor on an abstract class doesn't make any sense because you can't instantiate an abstract class directly (can only instantiate through a derived type that itself is not marked as abstract).

I think it would be good to clarify the last sentence, to state that this is only in this example, that generally abstract classes have default constructors if none have been explicitly declared

Along similar lines to the NOTE and Vic's comment, if an abstract class extends another class which doesn't have a default constructor, then the abstract class must have a constructor which calls the non-default constructor of the class it's extending.

You would define a constructor in an abstract class if you are in one of these situations:

  • you want to perform some initialization (to fields of the abstract class) before the instantiation of a subclass actually takes place
  • you have defined final fields in the abstract class but you did not initialize them in the declaration itself; in this case, you MUST have a constructor to initialize these fields
  • you may define more than one constructor (with different arguments)
  • you can (should?) define all your constructors protected (making them public is pointless anyway)
  • your subclass constructor(s) can call one constructor of the abstract class; it may even have to call it (if there is no no-arg constructor in the abstract class)

In any case, don't forget that if you don't define a constructor, then the compiler will automatically generate one for you (this one is public, has no argument, and does nothing).

Yes it can have a constructor and it is defined and behaves just like any other class's constructor. Except that abstract classes can't be directly instantiated, only extended, so the use is therefore always from a subclass's constructor.

Yes! Abstract classes can have constructors!

Yes, when we define a class to be an Abstract Class it cannot be instantiated but that does not mean an Abstract class cannot have a constructor. Each abstract class must have a concrete subclass which will implement the abstract methods of that abstract class.

When we create an object of any subclass all the constructors in the corresponding inheritance tree are invoked in the top to bottom approach. The same case applies to abstract classes. Though we cannot create an object of an abstract class, when we create an object of a class which is concrete and subclass of the abstract class, the constructor of the abstract class is automatically invoked. Hence we can have a constructor in abstract classes.

Note: A non-abstract class cannot have abstract methods but an abstract class can have a non-abstract method. Reason is similar to that of constructors, difference being instead of getting invoked automatically we can call super(). Also, there is nothing like an abstract constructor as it makes no sense at all.

Читайте также: