Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторная 8. Коллекции в java.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
279.3 Кб
Скачать

Абстрактные классы

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

Например, классы Point (точка), Circle (круг) и Rectangle (прямоугольник) унаследованы от класса Figure (фигура). В классе Figure есть метод paint(), общий для всех подклассов — он нужен, чтобы нарисовать фигуру. Но между рисованием круга, точки и прямоугольника нет ничего общего, поэтому бессмысленно программировать этот метод в классе Figure.

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

Класс, в котором есть хотя бы один абстрактный метод, называется абстрактным классом и перед словом class должно также стоять слово abstract.

Создать объект абстрактного класса нельзя. Можно только унаследовать от этого класса другие классы, переопределить в них абстрактные методы (наполнив их конкретным содержимым) и создавать объекты уже этих классов.

Понятие интерфейса в Java. Описание интерфейса

Интерфейс представляет собой класс, в котором все поля — константы (т.е. статические — static и неизменяемые — final), а все методы абстрактные.

При описании интерфейса вместо ключевого слова class используется ключевое слово interface, после которого указывается имя интерфейса, а затем, в фигурных скобках список полей-констант и методов. Никаких модификаторов перед объявлением полей и методов ставить не надо: все поля автоматически становятся public static final, а методы — public abstract. Методы не могут иметь реализации, т.е. после закрывающей круглой скобки сразу ставится точка с запятой.

Опишем, например, интерфейс для объекта, который «умеет» сообщать информацию о себе в формате прайс-листа (т.е. сообщать свое название, цену, и краткое описание).

public interface PriceItem

{ String getTitle(); int getPrice(int count);

String getDescription(); }

Метод getPrice() в этом примере требует один целочисленный параметр (количество единиц товара).

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

Реализация интерфейса

Класс может реализовывать интерфейс, т.е. перенимать его поведение. Эта процедура аналогична наследованию, только вместо ключевого слова extends используется ключевое слово implements. Но если после extends может быть указан только один класс, то после implements можно перечислить через запятую произвольное количество интерфейсов. Класс может одновременно наследовать от другого класса и реализовывать интерфейсы.

Мы можем взять любой существующий в программе класс и «научить» его сообщать о себе все необходимые сведения.

Опишем для примера класс Dog (собака). У него будет два поля: кличка и возраст. При описании поведения собаки в этом простом примере ограничимся лаем. Конечно, лаять по-настоящему наша собака не будет (ведь это всего лишь программная конструкция), она будет выводить в консоль «гав-гав». Чтобы было интереснее, предположим, что все собаки, с которыми имеет дело наша программа, умны настолько, что когда их вынуждают лаять, они говорят «гав-гав» столько раз, сколько им лет.

class Dog

{ int age; // возраст

String name; // кличка

public void voice() {

for (int i = 1; i <= age; i++) { System.out.println("гав-гав"); } } }

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

public class Dog2 extends Dog implements PriceItem {

int price; Dog2(String n,int a,int p)

{name=n;age=a;price=p;};

public String getTitle() { return (name+"Умная собака"); };

public int getPrice(int count) { return price * count; };

public void setPrice(int p) { price = p; }

public String getDescription() {

return (getTitle()+", которая знает свой возраст "+Integer.toString(age)+

" и умеет сообщать его с помощью лая"); } }

Класс Dog2 «умеет» то же самое, что и старый класс Dog, но помимо этого его можно использовать в программе Интернет-магазина для формирования прайса. Обратите внимание, класс Dog ничего не знал о цене, поэтому понадобилось добавить метод setPrice() и поле price, чтобы эту цену можно было бы изменять. Точно также можно взять множество других классов, не имеющих отношения к собакам — велосипеды, футболки, компьютеры — и сделать их пригодными для нашей новой программы с помощью интерфейса PriceItem и механизма наследования.

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

Напишем класс, создающий экземпляры и вызывающий методы Dog2.

public class Inter {

public static void main(String[] args) {

PriceItem pi; // переменная интерфейсного типа

Dog2 dog = new Dog2("KAT",1,2);

// создается объект класса Dog2, на него ссылается переменная dog dog.voice(); // можно вызвать метод лая

System.out.println(dog.getTitle()); // можно вывести название

Dog oldDog = dog; // переменная oldDog ссылается на тот же самый объект oldDog.voice(); // можно работать с объектом нового класса по-старому

pi = dog; // переменная pi рассматривает тот же самый объект как товар для прайса

//pi.voice();

// НЕ ПОЛУЧИТСЯ. Этого метода нет в интерфейсе PriceItem

//Мы можем поместить собак, велосипеды и компьютеры в один массив goods (товары) и в цикле сформировать прайс:

Dog2[] goods=new Dog2[2]; // создание и заполнение массива элементами,

// поддерживающими интерфейс PriceItem

goods[0]=new Dog2("Дружок",1,2);

goods[1]=new Dog2("Шарик",2,4);

for (Dog2 g: goods)

{ System.out.println("Название: "

+ g.getTitle()

+ ", цена за единицу товара: "

+ g.getPrice(1) + ", описание: " + g.getDescription() + g.voice()+"."); }

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