Что такое абстрактный класс в 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());
}
}
- теперь можно перейти к созданию наследников. В данном случае это, например, подклассы Прямоугольник, Круг и Треугольник. В подклассах должны быть реализованы методы подсчета площади и периметра, также нужно добавить новые свойства, характерные для различных фигур:
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;
}
}
// Подобным образом можно создавать подклассы и для других фигур
- Далее можно начинать работу с объектами и методами:
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
В качестве полноценного примера рассмотрим абстрактный класс Животное и его наследников:
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
Цвет: зеленый
Попугай чирикает!
*\
Что иллюстрирует этот код:
- сначала создается абстрактный класс Animal — шаблон, в котором заданы общие для всех животных свойства: имя и возраст;
- методы makeSound() и displaySubclassesInfo() реализуются внутри наследников в соответствии с их особенностями — в данном случае животные издают разные звуки и имеют разные дополнительные характеристики, например, для собак это порода;
- метод displayInfo() выводит на экран всю информацию о животных, в том числе дополнительные характеристики за счет вызова displaySubclassesInfo() внутри. Так сокращается дублирование кода и нет необходимости создавать метод для отображения информации внутри каждого наследника. Также здесь поддерживается принцип сокрытия;
- к объектам разных подклассов можно обращаться через общий интерфейс;
- код из этого примера легко расширить: можно добавлять другие подклассы, например, Лошадь, Корова и так далее. При этом вносить изменения в уже существующий код не нужно.