Листинг 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