ВУЗ: Университет управления «ТИСБИ»
Категория: Учебное пособие
Дисциплина: Объектно-ориентированное программирование
Добавлен: 20.10.2018
Просмотров: 933
Скачиваний: 15
метаданных. Например, второй элемент в массиве указателей на
графические объекты (как это видно из рисунка) адресует набор из четырех
целых чисел, которые должны быть интерпретированы как параметры
объекта-эллипса, тогда как другая четверка чисел, адресуемая четвертым
элементом массива определяет уже другой объект, а именно –
прямоугольник.
С другой стороны, в любой момент можно изменить классовый тип
любого элемента массива указателей – достаточно присвоить ему адрес
другого объекта.
Теперь можно вернуться к ответу на вопрос о проверке динамического
типа полиморфных объектных переменных. Для этих целей в объектных
языках имеются специальные проверочные операторы:
оператор is в языках C# и Delphi / FreePascal
оператор instanceof в языке Java
Оператор is является логическим и поэтому чаще всего используется в
условном операторе следующим образом:
if ( объект is класс) then . . .
Оператор is возвращает истину, если объектная переменная слева от него
адресует в данный момент объект класса справа от него ИЛИ объекты
производного от него класса. Можно сказать, что оператор выполняет
проверку типа объектной переменной «с точностью до подиерархии», т.е.
фактически проверяется «попадание» объектной переменной в заданную
подиерархию, корнем которой является указанный в операторе класс.
Например, для иерархии графических фигур условный оператор вида
if (Figs[i] is TCircle) then …
возвращает истинное значение, если полиморфная переменная Figs[i] в
данный момент адресует окружность, эллипс, дугу или какой-то другой
объект дочернего для окружности класса. Если же Figs[i] адресует что-то «не
округлое» (например, отрезки или прямоугольники), оператор is вернет
ложное значение.
Этот прием можно использовать для выборочной обработки объектов
некоторой подиерархии. Например, для массива полиморфных указателей на
графические
объекты
можно
организовать
перемещение
только
криволинейных объектов, т.е. окружностей, эллипсов и дуг:
for i := 1 to N do
if (Figs[i] is TCircle) then Figs[i].MoveTo (dx, dy);
for (int i = 0; i < N; i++)
if (Figs[i] is Circle) Figs[i].MoveTo (. . .);
Аналогичная проверочная конструкция в языке Java выглядит так:
if (Figs[i] instanceof Circle) …..
При использовании полиморфных объектных переменных возникает еще
один любопытный вопрос: если объектная переменная адресует объект
«чужого» класса, то можно ли ее использовать для вызова методов, которых
нет в ее «родном» классе, но которые есть в текущем динамическом классе?
Например, можно ли с помощью полиморфного указателя Figs[i] вызвать
специфический метод класса окружностей, такой как ―Изменение радиуса‖
(ясно, что в родном классе фигур ничего подобного нет и быть не может)?
Для положительного ответа на этот вопрос необходимо:
1. Убедиться (например, с помощью оператора is), что текущее
значение указателя является правильным с точки зрения вызова метода
(например, Figs [i] адресует окружность)
2. Выполнить специальную операцию – приведение типов, т.е. привести
общий родительский указатель к конкретному дочернему типу
В языках C# и Delphi/FreePascal для явного приведения типов
используется специальный оператор as, который включается в следующую
синтаксическую конструкцию:
(объект as класс),
в результате чего объектная переменная приводится к типу указанного
справа класса. А вот после этого уже можно вызывать уникальные методы
того класса, к которому приведена данная объектная переменная, используя
обычный (т.е. через точку) синтаксис вызова метода:
(объект as класс).SomeChildMethod ( );
Возвращаясь опять к примеру с массивом указателей на графические
объекты, можно поставить следующую задачу: пройти по всем элементам
массива, проверить для каждого текущий классовый тип и для всех
криволинейных объектов выполнить изменение радиуса, а для всех
прямоугольных объектов – операцию поворота. Вот эта конструкция:
if (Figs[i] is TCircle) then (Figs[i] as TCircle).ChangeRad (…)
else if (Figs[i] is TRect) then (Figs[i] as TRect).Rotate (…);
Здесь конструкция (Figs[i] as TCircle) выполняет явное приведение
типа родительского указателя к классу TCircle, после чего с помощью этого
указателя выполняется обращение к методу изменения радиуса класса
окружностей, тогда как аналогичная конструкция (Figs[i] as TRect)
выполняет приведение уже к другому классу, а именно – к классу
прямоугольников.
Аналогичная конструкция в языке Java выглядит так:
if (Figs[i] instanceof Circle) (Circle (Figs[i] ) ).ChangeRad (…);
Для более глубокого проникновения к метаданным можно использовать
методы специальных классов, входящих в состав стандартных библиотек. В
языке Java такой класс носит простое и скромное имя Class, а в языке С# -
имя Type. Объекты этих классов создаются автоматически при создании
первого объекта каждого используемого в программе класса. Все
последующие объекты будут связаны с уже созданным объектом класса
Class или Type. Можно сказать, что доступ к метаданным осуществляется на
объектном уровне.
В языке Java для доступа к объектам класса Class можно либо
использовать встроенное поле с именем class, которым автоматически
снабжаются объекты всех классов, либо метод getClass( ), который объявлен
в корневом классе Object и наследуется всеми его потомками. В классе Class
реализовано несколько методов, наиболее полезными из которых являются:
forName(String name) – возвращает указатель на объект класса Class
для класса с именем name; метод является статическим (классовым) и
поэтому может использоваться БЕЗ создания базового объекта;
getName( ) – возвращает имя класса для вызывающего объекта;
getSuperclass( ) – возвращает указатель на родителя вызывающего
объекта;
Кроме того, предусмотрены методы, которые формируют списки всех
полей, методов, конструкторов и интерфейсов класса.
Аналогичные функции в языке C# обеспечиваются стандартным
классом Type. Он включает целый ряд свойств и методов, таких как:
GetFields ( ) – получение списка полей указанного класса
GetMethods ( ) – получение списка методов класса
GetConstructors ( ) – получение списка конструкторов
GetEvents ( ) – получение списка событий
GetProperties ( ) – получение списка свойств
Для использования всех этих методов прежде всего надо получить
объект класса Type для интересующего нас класса. Для этого можно
использовать специальный оператор typeof (ИмяКласса). Например, если нам
нужна информация по классу с именем MyClass, то объект-носитель этой
информации инициируется следующим образом:
Type MyTypeObject = typeof (MyClass);
После этого конструкция MyTypeObject.Name позволит получить имя
класса, а конструкция MyTypeObject.GetMethods ( ) – список всех его
методов.
Все это позволяет динамически во время выполнения программы
получать всю информацию о любом используемом объекте программы.
Важность этого механизма определяется тем, что он активно используется
при компонентной разработке приложений в рамках визуальных
инструментов быстрого создания программ.