Включите исполнение JavaScript в браузере, чтобы запустить приложение.
19 фев 2025

Абстрактный класс в Java: что это и зачем он нужен

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

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

Абстрактные классы в Java — это классы, которые нужны для описания базового функционала будущей группы подклассов. При этом создать экземпляр такого класса нельзя. Другими словами, это некий шаблон, в котором могут быть определены общие поля и методы, которые понадобятся всем наследникам, а также абстрактные методы, которые наследники реализуют. Например, можно создать абстрактный класс Человек с полями имя и возраст (так как все люди имеют это свойство) и методами, возвращающими эти значения. Наследниками же могут выступать классы: Школьник, Студент, Преподаватель, которые могут иметь собственные свойства и методы в дополнение к уже обозначенным в абстрактном классе.

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

Зачем нужен абстрактный класс

Рассмотрим основные задачи абстрактных классов:

  • создание шаблона для будущих наследников, который при этом не будет предоставлять им конкретных реализаций (то есть предоставляется только интерфейс). Следование шаблону повышает надежность и поддерживаемость кода, особенно если обязательные методы для наследников определены заранее. В то же время в подклассах могут изменяться/появляться новые свойства и методы, поэтому принципы объектно-ориентированного программирования (в частности, инкапсуляция и полиморфизм) не нарушаются;
  • улучшение структуры (организации) кода вследствие создания «групп» подклассов, которые наследуют от одного абстрактного класса. Также такой код легче расширять (создавать новых наследников) при необходимости, не внося изменений в уже существующую программу;
  • устранение дублирования кода за счет того, что в абстрактном классе можно реализовать методы, которые будут использоваться в подклассах.

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

Как использовать абстрактный класс в Java

Рассмотрим пошагово то, как применять абстрактные классы:

  • вначале необходимо определить свойства и методы, которые понадобятся каждому подклассу. Например, геометрические фигуры: каждая из них может иметь какой-то цвет (это свойство), а также формулы для расчета периметра и площади (это абстрактные методы, так как они реализуются по-разному для каждой фигуры). Далее можно создать абстрактный класс: в Java это делается с помощью abstract:
public abstract class Shape {

  protected String color;  // Цвет — это общее свойство для всех фигур

  // Конструктор 

  public Shape(String color) {

      this.color = color;

  }

  // Метод для подсчета площади (будет реализован в подклассах)

  public abstract double calculateArea();

  // Метод для подсчета периметра (будет реализован в подклассах)

  public abstract double calculatePerimeter();

  // Метод, который выводит информацию о фигуре на экран (он общий, но наследники могут

  // переопределить его)

  public void displayInfo() {

      System.out.println("Цвет: " + color);

      System.out.println("Площадь: " + calculateArea());

      System.out.println("Периметр: " + calculatePerimeter());

  }

}
java
  • теперь можно перейти к созданию наследников. В данном случае это, например, подклассы Прямоугольник, Круг и Треугольник. В подклассах должны быть реализованы методы подсчета площади и периметра, также нужно добавить новые свойства, характерные для различных фигур:
public class Rectangle extends Shape {

  private double width;  // Появляются новые свойства: ширина и длина

  private double length;  

  public Rectangle(String color, double width, double length) {

      super(color);

      this.width = width;

      this.length = length;

  }

  // Реализация методов вычисления площади и периметра

  @Override

  public double calculateArea() {

      return width * length;

  }

  @Override

  public double calculatePerimeter() {

      return 2 * (width + length);

  }

}

public class Circle extends Shape {

  private double radius;  // Для круга добавляется свойство радиус

  public Circle(String color, double radius) {

      super(color);

      this.radius = radius;

  }

  // Реализация методов вычисления площади и периметра

  @Override

  public double calculateArea() {

      return Math.PI * radius * radius;

  }

  @Override

  public double calculatePerimeter() {

      return 2 * Math.PI * radius;

  }

}

public class Triangle extends Shape {

  private double sideA;  // Свойства для треугольников — три стороны

  private double sideB;

  private double sideC;

  public Triangle(String color, double sideA, double sideB, double sideC) {

      super(color);

      this.sideA = sideA;

      this.sideB = sideB;

      this.sideC = sideC;

  }

  // Реализация методов вычисления площади и периметра

  @Override

  public double calculateArea() {

      // Так как известны только длины сторон, используется такая формула

      double s = (sideA + sideB + sideC) / 2; 

      return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC));  

  }

  @Override

  public double calculatePerimeter() {

      return sideA + sideB + sideC;

  }

}

// Подобным образом можно создавать подклассы и для других фигур
java
  • Далее можно начинать работу с объектами и методами:
public class Main {

  public static void main(String[] args) {

      Shape rectangle = new Rectangle("зеленый", 5, 3);

      Shape circle = new Circle("белый", 4);

      Shape triangle = new Triangle("черный", 3, 4, 5);

      // Вызов метода displayInfo() для одного из объектов

      rectangle.displayInfo();

  }

}
java

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

В качестве полноценного примера рассмотрим абстрактный класс Животное и его наследников:

public abstract class Animal {

  // Существуют характеристики, которыми можно описать любых животных, например, имя и возраст

  protected String name;

  protected int age;

  // Конструктор 

  public Animal(String name, int age) {

      this.name = name;

      this.age = age;

  }

  // Метод makeSound() должен быть реализован в подклассах

  public abstract void makeSound();

  // displaySubclassesInfo() будет выводить на экран информацию о характеристиках подклассов

  protected abstract void displaySubclassesInfo();

  // С помощью displayInfo информация о животных будет выводиться на экран

  public void displayInfo() {

      System.out.println("Имя: " + name);

      System.out.println("Возраст: " + age);

      displaySubclassesInfo();

  }

}

// Подкласс Собака

public class Dog extends Animal {

  private String breed;  //  Для собак добавлено свойство порода

  public Dog(String name, int age, String breed) {

      super(name, age);

      this.breed = breed;

  }

  @Override

  public void makeSound() {

      System.out.println("Собака гавкает!");

  }

  @Override

  protected void displaySubclassesInfo() {

      System.out.println("Порода: " + breed);

  }

}

// Еще один подкласс Кошка

public class Cat extends Animal {

  private String color;  // Добавлено свойство цвет

  public Cat(String name, int age, String color) {

      super(name, age);

      this.color = color;

  }

  @Override

  public void makeSound() {

      System.out.println("Кошка мяукает!");

  }

  @Override

  protected void displaySubclassesInfo() {

      System.out.println("Цвет: " + color);

  }

}

// Подкласс Попугай

public class Parrot extends Animal {

  private String color;  // Добавлено свойство цвет

  public Parrot(String name, int age, String color) {

      super(name, age);

      this.color = color;

  }

  @Override

  public void makeSound() {

      System.out.println("Попугай чирикает!");

  }

  @Override

  protected void displaySubclassesInfo() {

      System.out.println("Цвет: " + color);

  }

}

public class Main {

  public static void main(String[] args) {

      // Создание экземпляров и вызов методов для каждого из них

      Animal dog = new Dog("Шарик", 5, "лабрадор");

      Animal cat = new Cat("Дуся", 3, "серый");

      Animal parrot = new Parrot("Кеша", 2, "зеленый");

      dog.displayInfo();

      dog.makeSound();

      System.out.println();

      cat.displayInfo();

      cat.makeSound();

      System.out.println();

      parrot.displayInfo();

      parrot.makeSound();

  }

}

/* Вывод:

Имя: Шарик

Возраст: 5

Порода: лабрадор

Собака гавкает!

Имя: Дуся

Возраст: 3 

Цвет: серый

Кошка мяукает!

Имя: Кеша

Возраст: 2 

Цвет: зеленый

Попугай чирикает!

*\
java

Что иллюстрирует этот код:

  • сначала создается абстрактный класс Animal — шаблон, в котором заданы общие для всех животных свойства: имя и возраст; 
  • методы makeSound() и displaySubclassesInfo() реализуются внутри наследников в соответствии с их особенностями — в данном случае животные издают разные звуки и имеют разные дополнительные характеристики, например, для собак это порода;
  • метод displayInfo() выводит на экран всю информацию о животных, в том числе дополнительные характеристики за счет вызова displaySubclassesInfo() внутри. Так сокращается дублирование кода и нет необходимости создавать метод для отображения информации внутри каждого наследника. Также здесь поддерживается принцип сокрытия;
  • к объектам разных подклассов можно обращаться через общий интерфейс; 
  • код из этого примера легко расширить: можно добавлять другие подклассы, например, Лошадь, Корова и так далее. При этом вносить изменения в уже существующий код не нужно.