Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование на Java.docx
Скачиваний:
2
Добавлен:
01.05.2025
Размер:
3.45 Mб
Скачать

Тема 3.11 Использование абстрактных классов

В некоторых случаях необходимо создать суперкласс, который лишь определяет общий формат, детали же уточняются лишь в его подклассах. В таком суперклассе нет необходимости реализовывать методы, – эта задача делегируй дочерним классам. Иногда подобная потребность возникает потому, что в суперклассе попросту невозможно создать осмысленную реализацию метода. Таким является класс TwoDShape, рассмотренный в предыдущих примерах. В нем метод area() представляет собой не более чем заглушку. Он не способен вычислить и отобразить площадь конкретного объекта.

Когда вы начнете создавать свои библиотеки классов, то увидите, что ситуация, при которой в контексте суперкласса невозможно реализовать тот или иной метод, встречается очень часто. Справиться с такой проблемой можно двумя способами. Один из них был рассмотрен в предыдущем примере. Он состоит в том что метод лишь отображает предупреждающее сообщение. Такой подход может быть полезным в ряде случаев, например при отладке, но в большинстве ситуаций он совершенно неприменим. Поэтому чаще всего применяется второй способ, позволяющий создать метод, который должен быть переопределен в подклассе там, где он приобретает конкретный смысл. Рассмотрим класс Triangle. Он будет неполон, если не определить метод area(). Нужен механизм, позволяющий убедиться в том, что в подклассе переопределены требуемые методы. В языке Java этот механизм носит название абстрактный метод.

При создании абстрактного метода используется модификатор abstract. Тело абстрактного метода отсутствует, поэтому он не реализуется в суперклассе. В подклассе он должен быть переопределен. Использовать вариант метода, объявленный в суперклассе, невозможно. Абстрактный метод объявляется в следующем формате:

abstract тип имя (список_параметров);

Как видите, тело метода отсутствует. Модификатор abstract применим только с обычными методами. Он не может быть использован со статическими методами или с конструкторами.

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

Подкласс, наследующий абстрактный класс, должен реализовать все абстрактные методы. Если это не будет сделано, то подкласс также должен быть абстрактным. Таким образом, подклассы останутся абстрактными до тех пор, пока все абстрактные методы не будут реализованы. Теперь мы можем модифицировать класс TwoDShape, используя модификатор abstract. Поскольку говорить о площади фигуры невозможно до тех пор, пока тип фигуры не будет конкретизирован, в классе TwoDShape есть смысл добавить абстрактный метод area(), а в определении самого класса также использовать модификатор abstract. Это будет означать, что во всех подклассах класса TwoDShape должен быть переопределен метод area().

Листинг 3.19

// Создание абстрактного класса

public abstract class TwoDShape {

private double width;

private double height;

private String name;

// Конструктор по умолчанию

public TwoDShape() {

width = height = 0.0;

name = "null";

}

// Конструктор с параметрами

public TwoDShape(double w, double h, String n) {

width = w;

height = h;

name = n;

}

// Формирование объекта с одинаковыми значениями width и height

public TwoDShape(double x, String n) {

width = height = x;

name = n;

}

// Формирование объекта на основе другого объекта

public TwoDShape(TwoDShape ob) {

width = ob.width;

height = ob.height;

name = ob.name;

}

// Методы для доступа к переменным width и height

public double getWidth() {

return width;

}

public double getHeight() {

return height;

}

public void setWidth(double w) {

width = w;

}

public void setHeight(double h) {

height = h;

}

public String getName() {

return name;

}

public void showDim() {

System.out.println("Width and height are "

+ width + " and " + height);

}

// Теперь метод area() абстрактный

public abstract double area();

}

// Подкласс класса TwoDShape для представления треугольников

public class Triangle extends TwoDShape {

private String style;

// Конструктор по умолчанию

public Triangle() {

super();

style = "null";

}

// Конструктор класса Triangle

public Triangle(String s, double w, double h) {

super(w, h, "triangle");

style = s;

}

// Формирование равнобедренного треугольника

public Triangle(double x) {

super(x, "triangle"); // Вызов конструктора суперкласса

style = "isosceles";

}

// Формирование объекта на основе другого объекта

public Triangle(Triangle ob) {

super(ob); // Передача объекта конструктору TwoDShape

style = ob.style;

}

public double area() {

return getWidth() * getHeight() / 2;

}

public void showStyle() {

System.out.println("Triangle is " + style);

}

}

// Подкласс класса TwoDShape для представления прямоугольников

public class Rectangle extends TwoDShape {

// Конструктор по умолчанию

public Rectangle() {

super();

}

// Конструктор класса Rectangle

public Rectangle(double w, double h) {

super(w, h, "rectangle"); // Вызов конструктора суперкласса

}

// Формирование квадрата

public Rectangle(double x) {

super(x, "rectangle"); // Вызов конструктора суперкласса

}

// Формирование объекта на основе другого объекта

public Rectangle(Rectangle ob) {

super(ob); // Передача объекта конструктору TwoDShape

}

public boolean isSquare() {

if (getWidth() == getHeight()) {

return true;

}

return false;

}

public double area() {

return getWidth() * getHeight();

}

}

public class AbsShape {

public static void main(String args[]) {

TwoDShape shapes[] = new TwoDShape[4];

shapes[0] = new Triangle("right", 8.0, 12.0);

shapes[1] = new Rectangle(10);

shapes[2] = new Rectangle(10, 4);

shapes[3] = new Triangle(7.0);

for (int i = 0; i < shapes.length; i++) {

System.out.println("object is " + shapes[i].getName());

System.out.println("Area is " + shapes[i].area());

System.out.println();

}

}

}

Итак, в данной программе во всех подклассах класса TwoDShape должен б переопределен метод area(). Чтобы убедиться в этом, попробуйте создать класс, в котором определение метода area() будет отсутствовать. При попытке скомпилировать такую программу вы получите сообщение об ошибке. Несмотря на то что в классе TwoDShape отсутствует определение одного из методов, возможность создать ссылку на объект данного типа. Это также видно из текста программы. В отличие от предыдущих вариантов класса TwoDShape объект данного типа на этот раз сформировать невозможно. По этой причине число элементов массива shapes в методе main() сокращено до четырех; объект TwoDShape теперь не создается.

И еще одно замечание. Обратите внимание на то, что в классе TwoDShape по-прежнему присутствуют определения методов showDim () и getName () и перед ними нет модификатора abstract. Это допустимо; абстрактный класс имеет право содержать полностью определенные методы, которые могут быть или не быть переопределены в подклассах. Обязательному переопределению подлежат лишь те методы, перед которыми указан модификатор abstract.

Задание:

Напишите класс Собака, который наследуется от класса Животные. В классы Собака и Кот добавьте метод голос. В классе Животные этот метод абстрактный.