Помощничек
Главная | Обратная связь


Археология
Архитектура
Астрономия
Аудит
Биология
Ботаника
Бухгалтерский учёт
Войное дело
Генетика
География
Геология
Дизайн
Искусство
История
Кино
Кулинария
Культура
Литература
Математика
Медицина
Металлургия
Мифология
Музыка
Психология
Религия
Спорт
Строительство
Техника
Транспорт
Туризм
Усадьба
Физика
Фотография
Химия
Экология
Электричество
Электроника
Энергетика

ЛАБОРАТОРНАЯ РАБОТА №6



 

Тема: Полиморфизм.

Цель: научится использовать полиморфизм.

Теоретические сведения

Полиморфизм.

Рассматриваемые вопросы:

6.1 Полиморфизм

6.2 Перегрузка методов.

6.3 Динамическая диспетчеризация методов

6.4 Зачем нужны переопределенные методы?

6.5 Применение переопределения методов

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

Полиморфизм

Полиморфизм (polymorphism) – положение теории типов, согласно которому имена (например, переменных) могут обозначать объекты разных (но имеющих общего родителя) классов. Следовательно, любой объект, обозначаемый полиморфным именем, может по-своему реагировать на некий общий набор операций [2].

В процедурном программировании тоже существует понятие полиморфизма, которое отличается от рассмотренного механизма в ООП. Процедурный полиморфизм предполагает возможность создания нескольких процедур или функций с одним и тем же именем, но разным количеством или различными типами передаваемых параметров. Такие одноименные функции называются перегруженными, а само явление - перегрузкой (overloading). Перегрузка функций существует и в ООП и называется перегрузкой методов.

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

Предположим, мы хотим создать векторный графический редактор, в котором нам нужно описать в виде классов набор графических примитивов – Point, Line, Circle, Box и т.д. У каждого из этих классов определим метод draw для отображения соответствующего примитива на экране.

Очевидно, придется написать код, который при необходимости отобразить рисунок будет последовательно перебирать все примитивы, на момент отрисовки находящиеся на экране, и вызывать метод draw у каждого из них. Человек, незнакомый с полиморфизмом, вероятнее всего, создаст несколько массивов (отдельный массив для каждого типа примитивов) и напишет код, который последовательно переберет элементы из каждого массива и вызовет у каждого элемента метод draw. В результате получится примерно следующий код:

 

...

//создание пустого массива, который может

// содержать объекты Point с максимальным

// объемом 1000

Point[] p = new Point[1000];

 

Line[] l = new Line[1000];

Circle[] c = new Circle[1000];

Box[] b = new Box[1000];

// предположим, в этом месте происходит

// заполнение всех массивов соответствующими

// объектами

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

//цикл с перебором всех ячеек массива.

//вызов метода draw() в случае,

// если ячейка не пустая.

if(p[i]!=null) p[i].draw();

}

 

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

if(l[i]!=null) l[i].draw();

}

 

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

if(c[i]!=null) c[i].draw();

}

 

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

if(b[i]!=null) b[i].draw();

}

...

 

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

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

 

Рис. 3. Пример иерархии классов.

 

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

Для описанной выше иерархии классов, используя полиморфизм, можно написать следующий код:

 

...

Point p[] = new Point[1000];

p[0] = new Circle();

p[1] = new Point();

p[2] = new Box();

p[3] = new Line();

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

if(p[i]!=null) p[i].draw();

}

...

 

В описанном выше примере массив p[] может содержать любые объекты, порожденные от наследников класса Point. При вызове какого-либо метода у любого из элементов этого массива будет выполнен метод того объекта, который содержится в ячейке массива. Например, если в ячейке p[0] находится объект Circle, то при вызове метода draw следующим образом:

 

p[0].draw()

 

нарисуется круг, а не точка.

 

Примером использования перегрузки методов в языке Java может служить класс PrintWriter, который применяется, в частности, для вывода сообщений на консоль. Этот класс имеет множество методов println, которые различаются типами и/или количеством входных параметров. Вот лишь несколько из них:

 

void println()

// переход на новую строку

void println(boolean x)

// выводит значение булевской

// переменной (true или false)

void println(String x)

// выводит строку - значение

// текстового параметра.

 

Перегрузка методов.

Перегрузка методов поддерживает полиморфизм, потому что это один из способов, с помощью которых Java реализует парадигму «один интерфейс, множество методов». Чтобы понять, как это делается, приведем следующие рассуждения. На языках, которые не поддерживают перегрузку методов, каждому методу необходимо давать уникальное имя. Однако часто нужно реализовать, по существу, один и тот же метод для различных типов данных. Рассмотрим функцию абсолютного значения. На языках, которые не поддерживают перегрузку, существует обычно три или более версий этой функции, каждая со слегка отличающимся именем. Например, в C, функция abs() возвращает абсолютное значение целого числа, labs() возвращает абсолютное значение длинного целого числа, a fabs() — абсолютное значение числа с плавающей точкой. Так как C не поддерживает перегрузку, каждая функция должна иметь свое собственное имя, даже при том, что все три функции выполняют, по существу, одно и то же. Это делает ситуацию более сложной, чем она фактически есть на самом деле. Хотя основная концепция каждой функции одна и та же, вам все еще нужно помнить три разных имени. Подобная ситуация отсутствует в Java, потому что метод получения абсолютного значения един для всех типов данных. Действительно, библиотека стандартных классов Java включает метод абсолютного значения, с именем abs(). Этот метод перегружен в Math-классе Java, чтобы обрабатывать все числовые типы. Java определяет, какую версию abs() вызывать, основываясь на типе аргумента. Значение перегрузки заключается в том, что она позволяет осуществлять доступ к связанным методам при помощи общего имени. Таким образом, имя abs представляет общее выполняемое действие. Право же выбирать правильную специфическую версию для конкретного обстоятельства предоставлено компилятору. Вы же, как программист, должны только помнить общую выполняемую операцию. При использовании полиморфизма несколько имен были сокращены до одного. Хотя этот пример довольно прост, но если расширить концепцию, то можно увидеть, как перегрузка может помочь вам управлять большей сложностью.

 




Поиск по сайту:

©2015-2020 studopedya.ru Все права принадлежат авторам размещенных материалов.