Файл: 9. Полиморфизм на уровне объектных переменных.pdf

Добавлен: 20.10.2018

Просмотров: 837

Скачиваний: 15

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

 

 

метаданных.  Например,  второй  элемент  в    массиве  указателей  на 

графические объекты (как это видно из рисунка) адресует набор из четырех 

целых  чисел,  которые  должны  быть  интерпретированы  как  параметры 

объекта-эллипса,  тогда  как  другая  четверка  чисел,  адресуемая  четвертым 

элементом  массива  определяет  уже  другой  объект,  а  именно  – 

прямоугольник. 

С  другой  стороны,  в  любой  момент  можно  изменить  классовый  тип 

любого  элемента  массива  указателей  –  достаточно  присвоить  ему  адрес 

другого объекта. 

Теперь  можно  вернуться  к  ответу  на  вопрос  о проверке  динамического 

типа  полиморфных  объектных  переменных.  Для  этих  целей  в  объектных 

языках имеются специальные проверочные операторы: 

  оператор   is  в языках  C#  и  Delphi / FreePascal 

  оператор  instanceof  в языке Java 

 

Оператор   is является логическим и поэтому чаще всего используется в 

условном операторе следующим образом: 

if  ( объект  is  класс)  then . . . 

Оператор is возвращает истину, если объектная переменная слева от него 

адресует  в  данный  момент  объект  класса  справа  от  него  ИЛИ  объекты 

производного  от  него  класса.  Можно  сказать,  что  оператор    выполняет 

проверку  типа  объектной  переменной  «с  точностью  до  подиерархии»,  т.е. 

фактически  проверяется  «попадание»  объектной  переменной  в  заданную 

подиерархию, корнем которой является указанный в операторе класс. 

Например, для иерархии графических фигур условный оператор вида 

if  (Figs[i]  is  TCircle)  then … 

возвращает  истинное  значение,  если  полиморфная  переменная  Figs[i]  в 

данный  момент  адресует  окружность,  эллипс,  дугу  или  какой-то  другой 

объект дочернего для окружности класса. Если же Figs[i] адресует что-то «не 


background image

 

 

округлое»  (например,  отрезки  или  прямоугольники),  оператор    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. Выполнить специальную операцию – приведение типов, т.е. привести 

общий родительский указатель к конкретному дочернему типу 

 


background image

 

 

В  языках  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.  Объекты  этих  классов  создаются  автоматически  при  создании 

первого  объекта  каждого  используемого  в  программе  класса.  Все 


background image

 

 

последующие  объекты  будут  связаны  с  уже  созданным  объектом  класса 

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); 


background image

 

 

После  этого  конструкция    MyTypeObject.Name    позволит  получить  имя 

класса,  а  конструкция    MyTypeObject.GetMethods  (  )  –  список  всех  его 

методов.  

Все  это  позволяет  динамически  во  время  выполнения  программы 

получать  всю  информацию  о  любом  используемом  объекте  программы. 

Важность  этого  механизма  определяется  тем,  что  он  активно  используется 

при  компонентной  разработке  приложений  в  рамках  визуальных 

инструментов быстрого создания программ.