Может ли абстрактный класс иметь конструктор

Обновлено: 01.05.2024

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

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

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

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

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

Если ваш класс не объявляет конструктор, javac сделает для вас конструктор no-arg, do-nothing. Затем, когда ваш подкласс инициализирован, он вызовет сгенерированный конструктор no-op, и жизнь хороша.

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

конструкторы для абстрактных классов используются подклассами (вызываются из конструкторов подклассов с помощью super(params) ).

вы должны сделать эти конструкторы protected чтобы внести ясность.

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

использование может быть для инициализации общих атрибутов ie.

де-дублирование общих знаний / поведения.

Е. Г. автомобиля: все автомобили будут построены из тела и четырех колес и двигателя. Таким образом, Вы делаете эту часть конструкции в конструкторе для абстрактного класса автомобиля, вызывая такие функции, как Body (), Wheel(int x), Engine(). Каждый конкретный класс автомобилей будет иметь свою собственную реализацию Body (), Wheel() и Engine () - но все они будут делать те же шаги, чтобы построить автомобиль из них, поэтому нет необходимости дублировать эти шаги в каждом из этих классов. В этом случае вы реализуете это общее поведение в предке.

Я согласен, конструкторы создаются при условии, что будут экземпляры. Если у вас много общего кода, Вы можете подумать о создании конструктора, но гораздо лучше поместить его в init() метод.

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

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

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

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

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

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

2 ответа 2

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

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

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

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

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

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

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

В чем же тогда суть запрета создавать экземпляр абстрактного класса? Я просто не могу написать в коде new Calendar()? А через какие-то другие методы могу спокойно получить экземпляр такого класса? То есть верно ли утверждение, что "если класс является абстрактным, то его конструктор по умолчанию является protected"?

2 ответа 2

Если ты заглянешь в реализацию getInstance, то увидишь, что этот метод вызывает другой статичный метод createCalendar, а уже он создает не объекты абстрактного класса Calendar, а объекты наследников этого класса, например BuddhistCalendar. Так как BuddhistCalendar наследует Calendar, то позволяется неявное приведение к этому типу.

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

Например, если у вас есть List l , то реальный объект по ссылке может быть, например, типа ArrayList .

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

Получаем вывод (в моём случае) java.util.GregorianCalendar[. — экземпляр производного типа.

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

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.

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: Теперь, для реализации этого метода необходимо расширить абстрактный класс и этот метод переопределить.

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