Кроме обычных классов в Java есть абстрактные классы. Абстрактный класс похож на обычный класс. В абстрактном классе также можно определить поля и методы, но в то же время нельзя создать объект или экземпляр абстрактного класса. Абстрактные классы призваны предоставлять базовый функционал для классов-наследников. А производные классы уже реализуют этот функционал.
При определении абстрактных классов используется ключевое слово abstract:
abstract class имя_класса{
// содержимое класса
}
Обычно абстрактные классы создаются для таких сущностей, которые представляют лишь абстракцию и в реальности не имеют конкретного воплощения. Хрестоматийным примером является система геометрических фигур. В реальности не существует геометрической фигуры как таковой. Есть круг, прямоугольник, квадрат, но просто фигуры нет. Однако же и круг, и прямоугольник имеют что-то общее и являются фигурами.
Также абстрактные классы могут представлять и, на первый взгляд, конкретные сущности, но объекты которых не будут создаваться в программе и которые должны быть лишь основой для создания классов-наследников.
Например, определим абстрактный класс геометрической фигуры:
abstract class Shape{
int x; // x-координата точки
int y; // y-координата точки
Shape(int x, int y){
this.x = x;
this.y = y;
}
void print(){ System.out.println("Геометрическая фигура"); }
}
Геометрические фигуры, как правило, имеют некоторую точку, относительно которой располагается фигура. Например, для прямоугольника это обычно координаты верхнего левого угла, для круга - это центр и т.д.
И в данном случае с помощью полей x и y сохраняем эти координаты, которые передаются через конструктор. А для вывода информации о фигуре предназначен метод print()
Особенностью абстрактных классов является то, что мы не можем использовать их конструктор напрямую для создания объекта класса:
class Program{
public static void main(String[] args) {
// так нельзя
Shape someShape = new Shape(10, 15); // ! Ошибка: Shape is abstract; cannot be instantiated
}
}
Мы можем вызвать конструктор абстрактного класса лишь в производных классах. Создадим пару производных классов:
// абстрактный класс фигуры
abstract class Shape{
int x; // x-координата точки
int y; // y-координата точки
Shape(int x, int y){
this.x = x;
this.y = y;
}
void print(){ System.out.println("Геометрическая фигура"); }
}
// производный класс прямоугольника
class Rectangle extends Shape
{
int width;
int height;
// конструктор с обращением к конструктору класса Figure
Rectangle(int x, int y, int width, int height){
super(x,y);
this.width = width;
this.height = height;
}
@Override
void print(){
System.out.printf("Пpямоугольник. Верхний левый угол: (%d, %d); Ширина: %d; Длина: %d\n", x, y, width, height);
}
}
// производный класс круга
class Circle extends Shape
{
int radius;
// конструктор с обращением к конструктору класса Shape
Circle(int x, int y, int radius){
super(x,y);
this.radius = radius;
}
@Override
void print(){
System.out.printf("Круг. Центр: (%d, %d); Радиус: %d\n", x, y, radius);
}
}
В данном случае определены класс прямоугольника и класс круга. Они наследуют функционал абстрактного класса, добавляя дополнительные поля типа ширины и длины или радиуса, а также переопределяя
метод print(). Далее мы можем использовать объекты этих классов как объекты типа Shape:
class Program{
public static void main(String[] args) {
Shape shape1 = new Rectangle(10, 15, 200, 150);
Shape shape2 = new Circle(50, 60, 200);
printShape(shape1);
printShape(shape2);
}
static void printShape(Shape shape){
shape.print();
}
}
Консольный вывод:
Пpямоугольник. Верхний левый угол: (10, 15); Ширина: 200; Длина: 150 Круг. Центр: (50, 60); Радиус: 200
Наряду с абстрактными классами в языке Java есть абстрактные методы. Такие методы также предваряются модификатором abstract, но при этом не имеют тела - не выполняют никаких конкретных действий:
abstract тип_метода имя_метода(параметры_метода);
Производный класс обязан переопределить и реализовать все абстрактные методы, которые имеются в базовом абстрактном классе. Также следует учитывать, что если класс имеет хотя бы один абстрактный метод, то данный класс должен быть определен как абстрактный.
Например, в примере с фигурами выше метод print() в классе Shape не выполняет какой-то полезной нагрузки. Производные классы его в любом случае переопределяют, выводя в нем свою информацию. И мы можем сделать его
абстрактным:
abstract class Shape{
int x;
int y;
Shape(int x, int y){
this.x = x;
this.y = y;
}
// абстрактный метод
abstract void print();
}
// прямоугольник
class Rectangle extends Shape
{
int width;
int height;
Rectangle(int x, int y, int width, int height){
super(x,y);
this.width = width;
this.height = height;
}
@Override
void print(){
System.out.printf("Пpямоугольник. Верхний левый угол: (%d, %d); Ширина: %d; Длина: %d\n", x, y, width, height);
}
}
// круг
class Circle extends Shape
{
int radius;
Circle(int x, int y, int radius){
super(x,y);
this.radius = radius;
}
@Override
void print(){
System.out.printf("Круг. Центр: (%d, %d); Радиус: %d\n", x, y, radius);
}
}
Причем производные НЕабстрактные классы обязаны реализовать абстрактный метод.
Другим примером абстрактных методов могут быть такие действия, которые опять же не имеют конкретного воплощения, являются абстракцией и зависят от конкретного класса. Например, у геометрической фигуры можно вычислить периметр и площадь. Но для прямоугольника эти показатели будут вычисляться одним образом, для круга - другим образом и т.д. Для абстрактной геометрической фигуры мы не можем определить универсальный способ вычисления периметра и площади, поэтому такие действия можно определить в виде абстрактных методов. А конкретные классы по своему их реализуют:
abstract class Shape{
int x;
int y;
Shape(int x, int y){
this.x = x;
this.y = y;
}
// абстрактный метод для вычисления периметра
abstract double getPerimeter();
// абстрактный метод для вычисления площади
abstract double getArea();
}
// прямоугольник
class Rectangle extends Shape
{
int width;
int height;
Rectangle(int x, int y, int width, int height){
super(x,y);
this.width = width;
this.height = height;
}
@Override
double getPerimeter(){ return this.width * 2 + this.height * 2; }
@Override
double getArea(){ return this.width * this.height; }
}
// круг
class Circle extends Shape
{
int radius;
Circle(int x, int y, int radius){
super(x,y);
this.radius = radius;
}
@Override
double getPerimeter(){ return this.radius * 3.14 * 2; }
@Override
double getArea(){ return this.radius * 3.14; }
}
Применение классов:
class Program{
public static void main(String[] args) {
Shape shape1 = new Rectangle(10, 15, 200, 150);
System.out.println("Площадь прямоугольника: " + shape1.getArea());
System.out.println("Периметер прямоугольника: " + shape1.getPerimeter());
Shape shape2 = new Circle(50, 60, 100);
System.out.println("Площадь круга: " + shape2.getArea());
System.out.println("Периметер круга: " + shape2.getPerimeter());
}
}
Консольный вывод программы:
Площадь прямоугольника: 30000.0 Периметер прямоугольника: 700.0 Площадь круга: 314.0 Периметер круга: 628.0