Что такое абстрактный класс в java
Перейти к содержимому

Что такое абстрактный класс в java

  • автор:

Что такое абстрактный класс в java

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

При определении абстрактных классов используется ключевое слово abstract :

public abstract class Human < private String name; public String getName() < return name; >>

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

Human h = new Human();

Кроме обычных методов абстрактный класс может содержать абстрактные методы . Такие методы определяются с помощью ключевого слова abstract и не имеют никакой реализации:

public abstract void display();

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

Зачем нужны абстрактные классы? Допустим, мы делаем программу для обслуживания банковских операций и определяем в ней три класса: Person, который описывает человека, Employee, который описывает банковского служащего, и класс Client, который представляет клиента банка. Очевидно, что классы Employee и Client будут производными от класса Person, так как оба класса имеют некоторые общие поля и методы. И так как все объекты будут представлять либо сотрудника, либо клиента банка, то напрямую мы от класса Person создавать объекты не будем. Поэтому имеет смысл сделать его абстрактным.

public class Program < public static void main(String[] args) < Employee sam = new Employee("Sam", "Leman Brothers"); sam.display(); Client bob = new Client("Bob", "Leman Brothers"); bob.display(); >> abstract class Person < private String name; public String getName() < return name; >public Person(String name) < this.name=name; >public abstract void display(); > class Employee extends Person < private String bank; public Employee(String name, String company) < super(name); this.bank = company; >public void display() < System.out.printf("Employee Name: %s \t Bank: %s \n", super.getName(), bank); >> class Client extends Person < private String bank; public Client(String name, String company) < super(name); this.bank = company; >public void display() < System.out.printf("Client Name: %s \t Bank: %s \n", super.getName(), bank); >>

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

// абстрактный класс фигуры abstract class Figure < float x; // x-координата точки float y; // y-координата точки Figure(float x, float y)< this.x=x; this.y=y; >// абстрактный метод для получения периметра public abstract float getPerimeter(); // абстрактный метод для получения площади public abstract float getArea(); > // производный класс прямоугольника class Rectangle extends Figure < private float width; private float height; // конструктор с обращением к конструктору класса Figure Rectangle(float x, float y, float width, float height)< super(x,y); this.width = width; this.height = height; >public float getPerimeter() < return width * 2 + height * 2; >public float getArea() < return width * height; >>

Что такое абстрактные классы и методы в Java

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

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

Если вы новичок в Java или хотите освежить знания о том, чем отличаются абстрактные классы или интерфейсы, то можете почитать руководство и на эту тему: Difference Between Interface and Abstract class in Java.

Абстрактные классы
Определение синтаксиса абстрактных классов в Java

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

public abstract class Shape < // class body >

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

Различия между абстрактными и конкретными классами

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

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

Когда использовать абстрактные классы: пример

Абстрактные классы часто применяются для определения общего интерфейса или такого поведения, которое может совместно использоваться сразу множеством субклассов. Например, представьте, что вы проектируете игру, в которой используются различные фигуры – скажем, круги, квадраты и треугольники. У всех этих фигур есть определённые общие свойства, например, периметр и площадь. Но у них могут быть и уникальные характеристики, такие как количество сторон и радиус.

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

public abstract class Shape < protected int x, y; public Shape(int x, int y) < this.x = x; this.y = y; >public abstract double getArea(); public abstract double getPerimeter(); >

В этом примере класс Shape содержит переменные экземпляра для координат x и y некоторой фигуры, а также абстрактные методы для вычисления площади и периметра. Определяя эти методы как абстрактные, мы позволяем каждому конкретному субклассу реализовать их по-своему.

Например, можно было бы создать субкласс Circle , расширяющий класс Shape и реализующий собственные версии методов getArea() и getPerimeter() :

public class Circle extends Shape < private double radius; public Circle(int x, int y, double radius) < super(x, y); this.radius = radius; >public double getArea() < return Math.PI * radius * radius; >public double getPerimeter() < return 2 * Math.PI * radius; >> 

Аналогично, можно было бы создать субклассы Square и Triangle , также реализующие собственные версии методов getArea() и getPerimeter() , в то же время наследующие координаты x и y от суперкласса Shape .

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

Абстрактные методы
Определение и синтаксис абстрактных методов в Java

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

public abstract double getArea();

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

Правила и ограничения при работе с абстрактными методами

Существует несколько правил и ограничений, применимых к абстрактным методам в Java:

  • Абстрактный метод не может быть объявлен как final или private.
  • Абстрактный метод обязательно должен объявляться внутри абстрактного класса.
  • Конкретный субкласс, расширяющий абстрактный класс, обязательно должен предоставлять реализацию для всех его унаследованных абстрактных методов.

Мы уже определили абстрактный класс Shape , содержащий абстрактные методы для вычисления площади и периметра фигуры:

public abstract class Shape < protected int x, y; public Shape(int x, int y) < this.x = x; this.y = y; >public abstract double getArea(); public abstract double getPerimeter(); >

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

public class Circle extends Shape < private double radius; public Circle(int x, int y, double radius) < super(x, y); this.radius = radius; >public double getArea() < return Math.PI * radius * radius; >public double getPerimeter() < return 2 * Math.PI * radius; >>

В данном примере класс Circle реализует методы getArea() и getPerimeter() при помощи собственных уникальных формул для расчёта площади круга и периметра окружности.
Аналогично, можно было бы создать субклассы Square и Triangle , реализующие собственные версии методов getArea() и getPerimeter().

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

Наилучшие практики

Работая с абстрактными классами и методами в Java, нужно держать в уме некоторые наилучшие практики. Вот некоторые рекомендации, которые помогут вам эффективно пользоваться этими мощными инструментами:

  1. Пользуйтесь абстрактными классами, чтобы моделировать иерархии взаимосвязанных классов: при помощи абстрактных классов очень удобно представить группу таких взаимосвязанных классов, которые совместно используют некий общий функционал. Создавая абстрактный класс, который определяет общие методы и свойства, вы тем самым избегаете дублирования, повышаете модульность вашего кода и облегчаете его поддержку.
  2. Пользуйтесь абстрактными методами, чтобы определять общее поведение: абстрактные методы полезны в тех случаях, когда требуется навязать конкретное поведение сразу для множества субклассов. Определяя абстрактный метод в абстрактном классе, вы можете гарантировать, что все субклассы (каждый по-своему) будут реализовывать одно и то же поведение.
  3. Не переборщите с абстрактными классами и методами: притом, что абстрактные классы и методы могут быть довольно мощными, важно не прибегать к ним сверх меры. Вообще, следует создавать абстрактный класс или метод только в том случае, когда на то есть явная причина. Если слишком активно ими пользоваться, то код может излишне усложниться, и его станет тяжелее поддерживать.
  4. Придерживайтесь соглашений об именованиях: подбирая имена для абстрактных классов и методов, важно придерживаться стандартных соглашений об именованиях, действующих в Java. Именем абстрактного класса должно быть абстрактное существительное (напр., “Shape”), а имена абстрактных методов должны быть глаголами (напр. “draw”).
  5. Документируйте ваш код: как и при работе с любым кодом, абстрактные классы и методы важно документировать, чтобы они были понятнее другим разработчикам. Снабжайте код чёткими и краткими комментариями, которые поясняли бы смысл поведения каждого класса и метода, а также их назначение.

Заключение

Итак, абстрактные классы и методы – это мощные инструменты для реализации абстракций в Java. Умело пользуясь ими, вы сможете создавать более модульный и удобный в поддержке код, который проще понимать и расширять. Если вы моделируете иерархии взаимосвязанных классов или определяете общее поведение, которое должно реализовываться сразу во множестве субклассов, то вам в этом могут особенно помочь абстрактные классы и методы. Пользуясь рекомендованными практиками, рассмотренными выше, вы сможете освоить этот важный аспект программирования на Java.

Часто задаваемые вопросы

• Могут ли у абстрактных классов быть конструкторы?

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

• Могут ли абстрактные классы реализовывать интерфейсы?
Да, абстрактные классы могут реализовывать интерфейсы, это могут делать вообще любые классы. Эта возможность может пригодиться, если вы хотите предоставить некое общее поведение сразу для нескольких классов, так, чтобы они реализовывали один и тот же интерфейс.

• Могут ли абстрактные методы быть статическими?
Нет, абстрактные методы не могут быть статическими, так как для их работы требуется вызвать экземпляр класса. Правда, статические методы могут вызывать абстрактные методы, если они реализованы в конкретных субклассах.

• Могут ли абстрактные классы быть финальными?
Нет, абстрактные классы не могут быть финальными, так как они предназначены именно для расширения субклассами. Если сделать абстрактный класс финальным, то такое расширение будет исключено.

• Когда следует использовать абстрактные классы вместо интерфейсов?
Абстрактные классы хорошо вам подойдут, если вы хотите предоставить некоторый общий функционал для множества классов, входящих в общую иерархию. Интерфейсы лучше подходят для определения такого набора, методы из которого разные классы могут реализовывать независимо. Правило такое: пользуйтесь абстрактными классами, когда хотите предоставить базовую реализацию для группы взаимосвязанных классов. Интерфейсы уместнее, когда нужно определить группу методов для реализации разрозненными классами, в каждом по-своему.

  • Блог компании Издательский дом «Питер»
  • Java
  • Совершенный код
  • ООП

Что такое абстрактные классы в Java и чем они отличаются от интерфейсов

Абстрактные классы — почти как интерфейсы, но дают дополнительные возможности.

Анастасия Хамидулина
Автор статьи
5 мая 2023 в 11:06

В объектно-ориентированном программировании есть четыре основополагающих принципа: композиция, полиморфизм, наследование и делегация. Их нужно эффективно реализовывать в Java. Для этого есть специальные инструменты — абстрактные классы, и любому программисту на Java нужно понимать, что это такое и как грамотно использовать эти инструменты.

Абстрактные методы и классы

Абстрактный класс объявляется при описании класса: добавляют оператора abstract перед оператором class. Абстрактные классы Java нельзя инициализировать как объект, но от них можно наследоваться.

Пример объявления абстрактного класса:

public abstract class GraphicObject < // объявление полей // объявление не абстрактных методов abstract void draw(); >

Следующий код выдаст ошибку компиляции:

GraphicObject object = new GraphicObject();

Пример класса, наследующего от абстрактного класса:

public class Circle extends GraphicObject < void draw() < System.out.println(“Нарисовали круг.”); >>

Абстрактный метод Java — это метод, который объявлен, но в нём не описана логика, то есть метод не реализован. В таком случае нет фигурных скобок, а после объявления метода сразу идет точка с запятой.

abstract void moveTo(double deltaX, double deltaY);

Абстрактный класс необязательно должен содержать абстрактные методы. Но если какие-то методы класса объявлены абстрактными, то и он должен быть таким.

Следующий код выдаст ошибку компиляции:

public class GraphicObject

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

Java-разработчик: новая работа через 11 месяцев
Получится, даже если у вас нет опыта в IT

�� Методы интерфейсов, которые не объявлены статическими (оператор static) или методами по умолчанию (оператор default), тоже абстрактные. Но задается это неявно: чтобы уменьшить количество кода, обычно не пишут оператор abstract у методов интерфейса. Хотя добавить его можно — это не вызовет ошибки компиляции.

Разобраться во всех тонкостях языка программирования Java можно на курсе Skypro «Java-разработчик». За несколько месяцев изучите типы данных и переменные, разберетесь в циклах и массивах. В программе — необходимый минимум теории и много практики. Уже в процессе обучения начнете писать код, тестировать его и исправлять ошибки. А результаты практических заданий сможете положить в портфолио, чтобы быстрее найти работу по новой специальности.

Абстрактные классы и интерфейсы: сравнение

Редакторы кода для программирования на JavaScript

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

Но есть важные отличия. В интерфейсах все поля по умолчанию публичные, статические и неизменяемые, а методы могут быть только публичными. В абстрактном классе можно объявить нестатическое поле класса изменяемым и непубличным. А методы абстрактного класса могут иметь все допустимые уровни доступа:

  • публичный (public);
  • приватный (private);
  • защищенный (protected);
  • на уровне пакета (package).

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

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

Когда использовать абстрактный класс, а когда — интерфейс

Используйте абстрактный класс, если к вашей задаче применимо одно или несколько условий:

�� Необходимо распределить некоторый общий код между несколькими тесно связанными классами.

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

�� У объекта класса какое-то внутреннее состояние. Это потребует объявления нестатических, изменяемых полей и методов доступа.

Используйте интерфейс, если к вашей задаче применимы утверждения:

�� Нужно объявить только контракт работы с каким-то типом данных без каких-либо указаний на его внутреннее состояние.

�� Объявленный контракт будет использоваться не только в связанных классах, но и в типах, которые напрямую не связаны с ним. Или даже в коде за пределами текущего приложения или библиотеки. Например, интерфейсы из стандартной библиотеки Java Comparable и Cloneable реализованы многими сторонними библиотеками.

�� Структура типа данных предполагает, что будет участвовать во множественном наследовании.

В качестве примера абстрактного класса из стандартной библиотеки Java приведем класс AbstractMap. У его наследников — HashMap, TreeMap и ConcurrentHashMap — много общих методов. Например, get, put, isEmpty, containsKey и containsValue, которые изначально были объявлены в AbstractMap.

Пример класса, который реализует несколько интерфейсов, — класс HashMap. Он реализует интерфейсы Serializable, Cloneable, и Map. Независимо от конкретной реализации этого класса он будет поддерживать клонирование, конвертацию в массив байтов. А еще иметь функции таблицы: предоставлять доступ к внутренним данным по ключу.

Многие библиотеки используют абстрактный класс и интерфейсы одновременно. Например, класс HashMap реализует несколько интерфейсов и наследует класс AbstractMap.

Почему в Java нет множественного наследования классов

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

В отличие от других популярных ООП-языков, таких как C++, Java не поддерживает множественное наследование, так как оно вызывает проблему ромба — или, по-другому, проблему алмаза.

Проблема ромба

Рассмотрим пример наследования, где дерево классов можно представить диаграммой:

Допустим, у нас есть абстрактный класс SuperClass. В нём объявлен метод, который реализуется классами-наследниками ClassA и ClassB:

SuperClass.java

public abstract class SuperClass

ClassA.java

public class ClassA extends SuperClass < @Override public void doSomething()< System.out.println("doSomething implementation of A"); >//ClassA own method public void methodA() > >

ClassB.java

public class ClassB extends SuperClass < @Override public void doSomething()< System.out.println("doSomething implementation of B"); >//ClassB specific method public void methodB() < >>

Теперь предположим, что при разрешенном множественном наследовании ClassC наследует оба класса: ClassA и ClassB.

ClassC.java

//Это гипотетический пример описания класса //В реальном компиляторе Java данный код вызовет //ошибку компиляции, public class ClassC extends ClassA, ClassB < public void test()< //calling super class method doSomething(); >>

Заметьте, что метод test() вызывает родительский метод doSomething(). Это ведет к неопределенности: мы не знаем, какой именно код будет выполнен в итоге, так как метод doSomething() реализован и в ClassA, и в ClassB. Компилятору потребуются дополнительные инструкции, чтобы разрешить эту ситуацию. Это и называется проблемой ромба.

Мы рассмотрели простой пример, но проблема ромба усугубляется, если наследоваться от трех и более классов, которые реализуют один и тот же метод. У разработчиков Java был выбор: простота языка или дополнительные языковые конструкции для поддержки множественного наследования. Они выбрали первое. Поэтому в Java нет множественного наследования, но его можно реализовать самостоятельно. Например, с помощью паттерна программирования «композиция».

Если хотите стать Java-разработчиком, пройдите курс Skypro. Учим с нуля даже тех, у кого вообще нет опыта в IT. Уже через 11 месяцев получите новую профессию, диплом о профпереподготовке, портфолио с проектами и работу. Не просто помогаем с поиском, а устраиваем на работу: гарантию фиксируем в договоре.

Пример абстрактного класса

Рассмотрим пример приложения для рисования. Его создают по принципам объектно-ориентированного программирования. В коде приложения нужно реализовать несколько классов, которые описывают графические объекты. Например, круг, прямоугольник, линию и кривую Безье.

У всех этих объектов должны быть общие атрибуты для описания состояния объекта:

  • позиция относительно сетки координат;
  • размер;
  • ориентация;
  • цвета обводки и цвет заполнения.

А еще методы для управления состоянием: передвинуть, повернуть, изменить размер, отрисовать.

Позиция, цвет заполнения и метод «передвинуть» будут одинаковыми для всех графических объектов. Другие придется реализовывать отдельно для каждого объекта. Например, «изменить размер» и «отрисовать».

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

Воспользуемся преимуществами абстрактного класса и реализуем все общие атрибуты и методы в общем предке. Назовем его GraphicObject. А всю различающуюся логику опишем в каждом классе-наследнике отдельно.

Структура дерева классов будет такая:

Сначала объявим класс GraphicObject, чтобы описать общие для всех потомков поля и методы: текущая позиция и метод для ее изменения (moveTo). В классе GraphicObject тоже объявим абстрактные методы с уникальной реализацией для каждого потомка. Например, методы draw or resize для отрисовки и изменения размера объекта соответственно. Код класса GraphicObject будет выглядеть примерно так:

abstract class GraphicObject

Все неабстрактные классы-потомки GraphicObject (Circle и Rectangle) должны содержать реализацию методов draw и resize. Их код будет выглядеть примерно так:

class Circle extends GraphicObject < void draw() < . >void resize() < . >>
class Rectangle extends GraphicObject < void draw() < . >void resize() < . >>

Если абстрактный класс реализует интерфейс

Когда класс реализует интерфейс, в нём нужно имплементировать все методы интерфейса. Но если объявить такой класс абстрактным, то некоторые или все методы можно не имплементировать. Вместо этого их можно реализовать через классы-наследники. Пример:

interface Y

abstract class X implements Y < @Override void methodA() < System.out.println(“Inside method A in class X”); >>
class XX extends X < @Override void methodB() < System.out.println(“Inside method B in class XX”); >>

В этом примере класс Х обязательно нужно объявить абстрактным, потому что он реализует только один из методов интерфейса Y. А класс ХХ, наследуя от класса Х, должен обязательно реализовать methodB().

Члены класса

У абстрактного класса могут быть статические поля и методы. Доступ к таким членам класса получают через ссылку на класс. При этом не создается его экземпляр. Например:

AbstractClass.staticMethod().

Главное про разницу между интерфейсом и абстрактным классом

  • Абстрактные классы похожи на интерфейсы, но в отличие от них поддерживают внутреннее состояние. А методы абстрактного класса могут иметь уровень доступа, отличный от публичного.
  • Абстрактные классы — важная часть парадигмы ООП, реализованной в Java. Как и абстрактные методы, их задают с помощью оператора abstract.
  • Абстрактные классы могут не содержать абстрактные методы и реализовывать интерфейсы.
  • Java не поддерживает множественное наследование, чтобы избежать проблемы ромба.

Абстрактные классы в Java и их отличия от интерфейсов: кратко и без воды

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

Иллюстрация: Dana Moskvina / Skillbox Media

Паша Лебедев

Паша Лебедев
Пишет на Java. Верит в Agile. Пьёт много кофе и не любит грязный код.

Абстрактный класс — это класс, который содержит методы без реализации. Но зачем он такой нужен? Давайте разбираться.

Представим, что нам нужно описать несколько животных — и у каждого будет свой класс. Мы соберём их характеристики, опишем в полях и методах. У некоторых животных характеристики будут совпадать — например, и у гепарда, и у коня 4 ноги. А у некоторых — не совпадут: кролик ест траву, а тигр — других животных.

Однако, несмотря на несовпадение, мы можем выделить шаблонные характеристики: количество лап, издаваемый звук, приём пищи, передвижение. Эти шаблонные характеристики мы можем собрать в одном месте — абстрактном классе «Животное» и указать компилятору, что наполним их конкретными данными и поведением позже — в классах отдельных животных или групп животных.

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

При этом абстрактным объект класса «Животное» создать нельзя — ведь в природе «просто животного» не существует, есть только волки, лисы, бегемоты и прочие слоны.

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

Давайте рассмотрим пример абстрактного класса в Java.

public abstract class Animal < Integer countOfLegs; Double weight; String color; public abstract void run( ); public void eat( ) < System.out.println("Animal says nyam-nyam"); > >

Как вы видите, абстрактный класс очень похож на обычный класс. Чтобы сделать класс абстрактным, нам нужно добавить ключевое слово abstract в описание класса и в методы, которые мы хотим оставить абстрактными, — то есть определить уже в конкретных классах (медведь, лиса, лягушка).

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

Именно так мы и сделали в примере выше: добавили переменные для веса, цвета и количества ног у животного, а также описали два метода: run («бежать») и eat («кушать»). В нашем случае метод run — абстрактный, а у метода eat есть реализация по умолчанию. Это значит, что в классах, которые наследуют от Animal, мы обязаны добавить определение метода run, а переопределять или нет метод eat, зависит от наших задач и желания.

Теперь давайте попробуем создать класс на основе абстрактного класса. Пусть это будет класс, описывающий собаку.

public class Dog extends Animal < @Override public void run() < System.out.println("Dog says gaf-gaf"); > >

Здесь extends Animal означает, что мы наследуем класс Animal. Как только мы создадим пустой класс Dog, компилятор сразу подсветит его красным и выведет такое сообщение:

Error: (3, 8) java: com.company.Dog is not abstract and does not override abstract method run () in com.company.Animal

Это значит, что мы обязаны переопределить метод run() — что мы и сделали с помощью аннотации @Override.

Теперь давайте создадим объект класса Dog. Как мы видим, после его создания нам доступны все переменные и методы из класса Animal. Попробуем вызвать eat и run.

public static void main(String[] args) < Dog scooby = new Dog(); scooby.eat(); scooby.run(); >

Вот что мы увидим в консоли:

Animal says nyam-nyam

Dog says gaf-gaf

Итак, давайте выделим важные моменты:

  • Мы не можем создавать объекты абстрактных классов — но в этом и нет смысла.
  • В абстрактный класс можно добавлять методы с реализацией по умолчанию.
  • Если мы наследуемся от абстрактного класса, то мы обязаны переопределить все абстрактные методы родительского класса (кроме тех, которые имеют реализацию по умолчанию) или сделать весь дочерний класс абстрактным.
  • Абстрактные методы не имеют тела.

Больше рецептов на Java — в нашей базе знаний.

Отличия абстрактного класса и интерфейса

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

  • Интерфейсы описывают только часть функциональности объекта — определённые признаки. Абстрактный класс же может описывать целую категорию разных объектов, а его характеристики имеют право наследовать только те объекты, которые являются частью этой категории. Например, собаки и волки — часть общей категории «Животные», а интерфейс, описывающий умение бегать, может реализовать и человек, и робот, и собака.
  • Интерфейс описывает только поведение (методы), и у него нет полей. Точнее, есть возможность их объявить, но они будут public static final. В то же время абстрактный класс может содержать классические поля, которые будут принадлежать разным объектам.
  • Наследник абстрактного класса обязан наследовать все его составляющие, а интерфейс создан только для реализации (имплементирования). Поэтому в Java мы можем наследовать класс только от одного класса, а на реализацию интерфейсов внутри одного класса ограничений нет.

Когда использовать абстрактный класс, а когда — интерфейс

Интерфейсы и абстрактные классы — это разные инструменты, разработанные для разных целей, хотя у них и есть общие черты.

Когда лучше использовать абстрактные классы:

  • Вы хотите избежать дублирования кода, реализуя несколько тесно связанных классов из одной семантической категории.
  • Классы, которые будут расширять ваш абстрактный класс, имеют много общих свойств и будут реализовывать много похожих методов.
  • Наследуемый класс используется в отношении IS-A — то есть класс-наследник только расширяет функциональность абстрактного класса.
  • Проект уже частично написан, а вы знаете, что выбранные классы будут часто меняться и дополняться новыми методами и полями. То есть поддерживать абстрактный класс и дополнять его намного проще, чем проектировать интерфейс и добавлять его новые методы во все места в коде, где они должны реализовываться.

Когда лучше использовать интерфейсы:

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

Это лишь базовые и самые распространённые ситуации. Решая конкретные задачи, лучше советоваться с более опытными разработчиками или принимать самостоятельное решение.

Заключение

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

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

  • Что такое класс в Java
  • Как разработчик на C++ превратил свой пет-проект в прибыльный стартап
  • Основные принципы ООП: большой гайд по объектно-ориентированному программированию

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *