Абстрактные классы — почти как интерфейсы, но у них есть дополнительные возможности.
В объектно ориентированном программировании четыре основных принципа: композиция, полиморфизм, наследование и делегация. Их нужно эффективно реализовывать в Java. Для этого есть специальные инструменты — абстрактные классы, и любому программисту нужно понимать, что это такое и как грамотно их использовать.
Абстрактные методы и классы
Абстрактные методы и классы в Java — это программы, которые создают иерархии классов и считаются основой наследования и полиморфизма.
- Класс в Java
Абстрактный класс в Java нельзя рассматривать как объект, но от него можно наследоваться. Это лишь основа для других видов. В родительском виде могут быть методы без реализации и с реализацией. - Метод в Java
Абстрактный метод — это алгоритм, который объявлен, но в нём не описана логика, то есть нет реализации. Это значит, что любой вид, который наследует общий тип, должен реализовать этот метод.
Вот как это выглядит на примере.
Нужно разработать программу, чтобы управлять разными животными.
- Создайте родительский вид
Животное
abstract class Животное {
// Абстрактный метод
abstract void издатьЗвук();
// Обычный метод
void описание() {
System.out.println("Это животное.");
}
}
- Потом два подвида:
Собака
иКошка
. Они наследуют отЖивотное
и реализуют базовый методиздатьЗвук
.
class Собака extends Животное {
@Override
void издатьЗвук() {
System.out.println("Собака гавкает.");
}
}
class Кошка extends Животное {
@Override
void издатьЗвук() {
System.out.println("Кошка мяукает.");
}
}
- В конце создайте объекты этих видов.
public class Main {
public static void main(String[] args) {
Животное собака = new Собака();
Животное кошка = new Кошка();
собака.описание(); // Вывод: Это животное.
собака.издатьЗвук(); // Вывод: Собака гавкает.
кошка.описание(); // Вывод: Это животное.
кошка.издатьЗвук(); // Вывод: Кошка мяукает.
}
}
Разобраться во всех тонкостях языка программирования Java и наконец-то сменить скучную работу на ту, что всегда нравилась, можно в онлайн-университете Skypro на курсе «Java-разработчик». За одиннадцать месяцев изучите типы данных и переменные, разберетесь в циклах и массивах. Практика на курсе занимает 70% от всего объема программы. Уже во время учебы начнете писать код, тестировать его и исправлять ошибки. Результаты практических заданий положите в портфолио, чтобы быстрее найти любимую работу по новой специальности.
Абстрактные классы и интерфейсы: сравнение
Главное отличие в том, что в интерфейсах все поля всегда публичные, статические и неизменяемые, а методы — только публичные. В абстрактном классе есть статические, изменяемые и непубличные поля. А методы могут быть с любым уровнем доступа: приватным, публичным, защищенным и на уровне пакета.
Характеристика | Абстрактные классы | Интерфейсы |
Объекты | Нельзя создать объект напрямую | Нельзя создать объект напрямую |
Методы | Могут быть родительские и реализованные | Только родительские, без реализации |
Поля | Содержат поля с разными уровнями доступа | Все поля публичные, статические и неизменяемые |
Наследование | Только от одного общего вида | Можно реализовать несколько интерфейсов |
Доступ к методам | Могут быть публичными, защищенными или приватными | Только публичные |
Когда использовать абстрактный класс, а когда — интерфейс
Интерфейсы подходят, когда нужно установить общий метод для классов.
- Переопределение метода интерфейса
Подходит, если нужно, чтобы разные виды реализовывали один и тот же вид, но с разными деталями. Например, есть интерфейс Движение с методом двигаться(). Класс Автомобиль реализует этот порядок так: «едет по дороге», а класс Велосипед — «крутится педалями». Оба вида используют один и тот же метод, но делают это по-разному. - Неизменяемые переменные
В интерфейсах можно объявлять неизменяемые переменные, или константы, которые будут доступны всем реализующим классам. Например, в интерфейсе ФизическиеКонстанты объявить константу PI — 3,14. Все типы, которые реализуют этот интерфейс, используют PI, но не могут менять его значение. - Default-методы
В default-методах есть реализация, но их можно переопределить в классе, который выполняет интерфейс. Например, в интерфейсе Устройство создать режим включить(). Общая реализация будет просто выводить «Устройство включено». Если есть класс Телевизор, можно переустановить этот метод и добавить, например, менять канал при включении.Абстрактные классы лучше использовать, когда есть несколько похожих видов с общими чертами и поведением. - Общие поля и методы
Родительские классы подойдут, когда нужно создать базовый вид с общими полями и методами для нескольких подвидов. В абстрактном классе Животное определить поле возраст и метод издатьЗвук(). Во всех подклассах, например Собака и Кошка, будет поле возраст, а режим издатьЗвук() реализован по-своему: Собака издавать звук «гав», а Кошка — «мяу». - Разные уровни доступа
Например, создать в общем типе Транспорт публичный прием ехать(). А еще защищенный режим рассчитатьРасход Топлива() и приватное поле скорость. Так можно контролировать, кто будет видеть и использовать разные методы и поля. - Наследование и реализация логикиЕсли в абстрактном классе Ученик есть режим учить(), то все виды, которые от него наследуют, например Студент и Школьник, могут добавить свою специфику и использовать этот метод.
Почему в Java нет множественного наследования классов
Множественное наследование — это процесс, когда класс наследует свойства и приемы от нескольких родительских классов. Например, тип A и B создают C, который наследует от обоих.
В отличие от других популярных языков программирования, например C++, Java не поддерживает множественное наследование. Это вызывает проблему ромба, или по-другому проблему алмаза.
Проблема ромба
Пример наследования, где дерево видов представлено в виде диаграммы:
Например, тип SuperClass. В нём объявлен метод, который реализуется классами-наследниками ClassA и ClassB:
SuperClass.java
ClassA.java
ClassB.java
Если множественное наследование разрешено, тогда ClassC наследует оба типа: ClassA и ClassB.
ClassC.java
Метод test()
вызывает родительский метод doSomething()
. Из-за этого может получиться путаница: непонятно, какой именно код будет выполнен в итоге. Так как метод doSomething()
реализован и в ClassA, и в ClassB. Компилятору нужны дополнительные инструкции, чтобы решить эту задачу. Это и называется проблемой ромба.
Проблема ромба может усугубиться, если наследовать от трех и более классов, которые реализуют один и тот же прием. У разработчиков Java был выбор: простой язык или дополнительные языковые конструкции — поддерживать множественное наследование. Специалисты выбрали первое. Поэтому в Java нет множественного наследования, но его можно создать самостоятельно. Например, через модель программирования «композиция».
Преподаватели на курсе «Java-разработчик» — действующие специалисты и опытные руководители в сфере разработки. Они объяснят все сложные термины простыми словами на примерах и поделятся профессиональными лайфхаками. Занятия проходят онлайн. Выбирайте, когда удобно учиться: утром, ночью или на выходных. Уроки доступны в нескольких форматах: видео, текстовые конспекты. Изучайте, как вам удобнее. После курса получите диплом о профессиональной переподготовке, а консультанты по карьере помогут составить резюме и пройти собеседование.
Пример абстрактного класса
Например, приложение для рисования. Его создают по принципам объектно ориентированного программирования. В коде программы нужно реализовать несколько видов, которые будут описывать графические объекты: круг, прямоугольник, линию и кривую Безье.
У всех этих объектов должны быть общие атрибуты:
- позиция относительно сетки координат,
- размер,
- ориентация,
- цвет обводки и заполнения.
А еще режим управлять состоянием: передвинуть, повернуть, изменить размер, отрисовать.
Позиция «цвет заполнения» и режим «передвинуть» будут одинаковыми для всех. Другие придется создавать отдельно для каждого элемента. Например, «изменить размер» и «отрисовать».
Все графические объекты должны уметь менять размер и отрисовывать себя, но разными способами. Это идеальная ситуация, чтобы использовать общий класс-родитель.
Создайте все атрибуты и приемы в общем предке. Назовите его GraphicObject. А логику, которая различается, опишите в каждом классе-наследнике отдельно.
Структура дерева будет такая:
Сначала объявлен тип GraphicObject — описаны общие для всех потомков поля и приемы: текущая позиция и режим изменения (moveTo). В классе GraphicObject тоже объявите родительские методы с уникальной реализацией для каждого потомка. Например, draw or resize для отрисовки и изменения размера объекта. Код GraphicObject будет выглядеть примерно так:
Во всех неабстрактных классах-потомках GraphicObject (Circle и Rectangle) должна быть реализация методов draw и resize. Их код будет выглядеть примерно так:
Если абстрактный класс реализует интерфейс
Когда класс реализует интерфейс, в нём нужно ввести все способы программы. Если, объявить такой вид общим, то некоторые методы можно не указывать. Вместо этого реализовать их через классы-наследники. Например:
В этом примере Х обязательно нужно объявить общим, потому что он создает только один из методов интерфейса Y. А ХХ наследует от класса Х и должен обязательно реализовать methodB().
Члены класса
У родительского класса могут быть статические поля и методы. Доступ к таким членам класса получают через ссылку. При этом его копия не создается. Например:
Главное про разницу между интерфейсом и абстрактным классом
- Абстрактные классы похожи на интерфейсы, только не поддерживают внутреннее состояние. А в методах есть уровень доступа, который отличается от публичного.
- Родительские классы — важная часть модели объектно ориентированного программирования, который реализован в Java. Как и базовые методы, их задают оператором abstract.
- Интерфейсы подходят, когда нужно задать набор методов, но без конкретной логики. А классы — создать базу с общими свойствами и методами.
- В родительских классах не обязательно должны быть общие методы и они могут не реализовывать интерфейсы.
- Java не поддерживает множественное наследование, чтобы избежать проблемы ромба.
Добавить комментарий