Листинг 8. Многоуровневое наследование, перегрузка и переопределение методов
class А{
void show(){
Sуstеm. оut. рrintln("Метод класса А");}
}
class В extends А{
void show(String msg){
System. out. println(msg);}
}
class С extends В{ void show(){
Sуstеm. оut. рrintln("Метод класса С");}
}
class MultiOverride{
public static void main(String args[]){
А obj1=new А();
В obj2=new В();
С оbj3=nеw С();
obj1.show();
obj2.show() ;
obj2.show( "Класс В");
оbj3.show();
оbj3.shоw("Класс С");}
}
Как и в предыдущем примере, создается иерархическая цепочка из трех классов; в вершине находится суперкласс А, на основе которого создается подкласс В, в свою очередь являющийся суперклассом для подкласса С. При этом классами наследуется, перегружается или переопределяется описанный в классе А метод show(). Схему перегрузки и переопределения этого метода иллюстрирует рис.1.

Рис.1. Схема перегрузки и переопределения метода show()
при многоуровневом наследовании
В частности, метод show() класса А не имеет аргументов и выводит сообщение Метод класса А. В классе В этот метод наследуется. Кроме того, в классе В метод show() перегружен с текстовым аргументом так, что он выводит сообщение, переданное в качестве его аргумента. Забегая наперед, отметим, что текстовый аргумент - это объект класса String. Текстовая строка, при передаче аргументом, заключается в двойные кавычки.
В классе С версия метода show() без аргумента переопределяется, а версия этого метода с текстовым аргументом наследуется из класса В.
В главном методе программы создаются три объекта - по объекту для каждого из классов. Затем из каждого объекта вызывается метод show() (с аргументами или без в зависимости от того, из какого объекта вызывается метод). В результате мы получаем следующее:
Метод класса А
Метод класса А
Класс В
Метод класса С
Класс С
Из объекта класса А вызывается версия метода без аргументов. Из объекта класса В метод вызывается без аргументов (версия метода из класса А) и с текстовым аргументом (версия метода, описанная в классе В). Вызываемая из объекта класса С версия метода без аргумента описана в классе С, а версия метода с текстовым аргументом наследуется из класса В.
Объектные переменные суперкласса и динамическое управление методами.
В наследовании было бы мало пользы, если бы не одно важное и интересное свойство объектных переменных суперкласса. Они могут ссылаться на объекты подкласса!
Напомним, что объектная переменная - это переменная, значением которой является ссылка на объект соответствующего класса, то есть фактически та переменная, которую мы отождествляем с объектом. Объектная переменная объявляется так же, как обычная переменная базового типа, с той лишь разницей, что в качестве типа переменной указывается имя класса. Создается же объект с помощью оператора new и конструктора класса. Сказанное означает, что ссылку на объект подкласса (объект, созданный конструктором подкласса) можно присвоить в качестве значения объектной переменной суперкласса (в качестве типа переменной указав имя суперкласса).
Важное ограничение состоит в том, что через объектную. переменную суперкласса можно ссылаться только на те члены подкласса, которые наследуются из суперкласса или переопределяются в подклассе. Пример приведен в листинге 9.
Листинг 9. Объектная переменная суперкласса ссылается на объект подкласса
class ClassA{
doublе Re;
void set(double х){
Re=x; }
void show(){
Sуstеm. out. рrintln("Класс А:");
sуstеm. оut. рrintln("Поле Re: "+Re);}
}
class ClassB extends ClassA{
doublе Im;
void set(double x, double у){
Re=x;
Im=у; }
void show(){
Sуstеm. оut. рrintln("Класс В:");
Sуstеm. оut. рrintln("Поле Re: "+Re);
Sуstеm. оut. рrintln("Поле Im: "+Im);}
}
class SuperRefs{
public static void main(String[] args){
ClassA objA;
ClassB objB=new ClassB();
objA=objB;
objB. set(1,5);
objB. show();
objA. set(-10);
objA. show();}
}
В данном случае описывается суперкласс СlassA, на основе которого создается подкласс ClassB. В суперклассе ClassA объявлено поле double Re и методы set() и show(). Метод show() не имеет аргументов и выводит сообщение с названием класса (буквы-идентификатора класса) и значением поля Re. Метод set() имеет один аргумент, который присваивается в качестве значения полю Re.
Поле Re наследуется в классе СlassB. В этом классе также описывается поле double Im Метод set() перегружается так, чтобы иметь два аргумента - значения полей Re и Im. Перегружается и метод show(), чтобы выводить на экран значения двух полей.
В главном методе программы командой СlassA objA объявляется объектная переменная objA класса СlassA. Командой СlassB objB=new СlassB() создается объект класса СlassB, и ссылка на этот объект присваивается в качестве значения объектной переменной objB класса СlassB. Затем командой objA=objB ссылка на тот же объект присваивается в качестве значения объектной переменной objA. Таким образом, в результате и объектная переменная objA, и объектная переменная objB ссылаются на один и тот же объект. То есть переменных две, а объект один. Тем не менее ссылка на объект через переменную objA является «ограниченной» через нее можно обращаться не ко всем. членам объекта класса СlassB.
Командой objВ. set(0,5) полям Re и Im объекта присваиваются значения 1 и 5 соответственно. Командой objB. show() значения полей объекта выводятся на экран. Для этого вызывается версия метода show(), описанная в классе ClassB. Командой objA. set(-10) меняется значение поля Re. Для этого вызывается версия метода set(), описанная в классе ClassA и наследуемая в классе СlassB. Вызвать через объектную переменную objA версию метода set() с двумя аргументами не получится - .эта версия не описана в классе classB, поэтому через объектную переменную суперкласса версия метода недоступна. Однако командой objA. show() можно вызватъ переопределенный в классе ClassB метод show(). Результат выполнения программы следующий:
Класс В:
Поле Re: 1.0
Поле Im: 5.0
Класс В:
Поле Re: -10.0
Поле Im: 5.0
Отметим также, что в силу отмеченных особенностей ссылки на объект подкласса чеёрез объектную переменную суперкласса через переменную objA можно обратиться к полю Re объекта подкласса, но нельзя обратиться к полю Im.
Хотя описанная возможность ссылаться на объекты подклассов через объектные переменные суперклассов может показаться не очень полезной, она открывает ряд перспективных технологий, в том числе и динамическое управление методами.
Динамическое управление методами базируется на том, что выбор варианта перегруженного метода определяется не типом объектной ссылки, а типом объекта, причем на этапе, не компиляции, а выполнения программы. С подобной ситуацией мы встречались в предыдущем примере, когда при ссылке на метод show() через объектную переменную objA суперкласса СlassA вызывалась переопределенная версия метода из подкласса СlassB, то есть версия, описанная в классе объекта, а не в классе объектной переменной. Рассмотрим еще один пример, представленный в листинге 10.
Листинг 10. Динамическое управление методами
c1assA{
void show(){
Sуstеm. оut. рrintln("Класс А");}
}
class В extends А{
void show(){
Sуstеm. оut. рrintln("Класс В");}
}
сlass С extends А{
void show() {
Sуstеm. оut. рrintln("Класс С");}
}
class Dispatch{
public static. vold main(String args[]){
А a=new А();
В b=new В();
С c=new С();
А ref;
ref=a;
ref. show();
ref=b;
ref. show();
rеf=с;
ref. show();}
}
В программе описывается суперкласс А, на основе которого создаются два класса: В и С. На рис.2 приведена общая иерархическая схема классов программы.

Рис.2. Структура классов при наследовании
В классе А описан метод show(), действие которого сводится к выводу на экран сообщения каждом из классов В и С этот метод переопределяется. Версия метода show() из класса В выводит сообщение Класс В, а версия этого же метода из класса С - сообщение
В главном методе программы создаются объекты а, b и с соответственно классов А, В и C, а также объявляется объектная переменная ref класса А. Далее этой объектной переменной последовательно в качестве значений присваиваются ссылки на объекты а, b и с (командами ref=a, ref=b и ref=c). Поскольку класс А является суперклассом и для класса В, и для класса с, данные операции возможны. Причем после. каждого такого присваивания через объектную переменную ref командой ref. show() вызывается метод show(). Результат выполнения программы имеет вид:
Класс А
Класс В
Класс С
Мы видим, что хотя формально во всех трех случаях команда вызова метода show() одна и та же (команда ref. show()), результат разный в зависимости от того, на какой объект в данный момент ссылается объектная переменная ref.
Абстрактные классы.
В Jаvа существуют такие понятия, как абстрактный метод и абстрактный класс. Под абстрактным методом подразумевают метод, тело которого в классе не объявлено, а есть только сигнатура ( тип результата, имя и список аргументов). Перед таким абстрактным методом указывается идентификатор abstract, а заканчивается описание сигнатуры метода в классе традиционно - точкой с запятой.
Класс, который содержит хотя бы один абстрактный метод, называется абстрактным. Описание абстрактного класса начинается с ключевого слова abstract.
Абстрактный класс в силу очевидных причин не может использоваться для создания объектов. Поэтому абстрактные классы являются суперклассами для подклассов. При этом в подклассе абстрактные методы абстрактного суперкласса должны быть определены в явном виде (иначе подкласс тоже будет абстрактным). Пример использования абстрактного класса приведен в листинге 11.
Листинг 11. Абстрактный класс
// Абстрактный суперкласс:
abstract class А{
// Абстрактный метод:
abstract void саllme();
// Неабстрактный метод:
void callmetoo(){
Sуstеm. оut. рrintln("Второй метод");}
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


