Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Пособие Java .pdf
Скачиваний:
118
Добавлен:
16.03.2015
Размер:
1.32 Mб
Скачать

ГЛАВА 4. НАСЛЕДОВАНИЕ

Одно из основных преимуществ ООП состоит в возможности наследования (inheritance), или расширения (extending) функционального потенциала базового (родительского – superclass) класса при построении на его основе производного (дочернего – subclass) класса. Объект нового класса допускается применять в любом контексте, где предусмотрено использование экземпляров исходного класса. Последняя возможность является частным случаем полиморфизма и означает, что объекты определённого класса способны восприниматься как сами по себе, так и в качестве экземпляра любого из исходных классов, от которых наследует действительный класс объекта.

Набор методов и полей класса, открытых для свободного доступа извне, в совокупности с описанием их назначения называют контрактом класса. Контракт – это способ выражения обещаний автора класса относительно того, на что способен и для чего предназначен созданный им продукт. Существуют две формы практиче-

ского воплощения концепции наследования:

наследование контракта или типа, в результате чего произ-

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

наследование способов реализации – производный класс при-

обретает реализацию базового в виде набора тел методов.

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

133

4.1Расширенный класс. Конструкторы расширенных классов

ВJava в случае наследования классов говорят, что один класс расширяет (extends) другой. Класс может расширить только один класс. В примере 61 класс MyClass2 расширяет (наследует от)

класса MyClass1.

Пример 61. Расширение класса. class MyClass1 {

}

class MyClass2 extends MyClass1{

}

Объект расширенного (производного) класса содержит поля, унаследованные от базового класса, и собственные переменные состояния. Чтобы создать объект расширенного класса, надлежит корректно проинициализировать оба набора переменных. Конструктор производного класса способен обращаться к полям базового, но только базовый класс «осведомлён» о том, как их следует инициализировать, чтобы гарантировать безупречное выполнение контракта класса. Конструкторы расширенного класса обязаны передавать полномочия по инициализации унаследованных полей, явно или косвенно обращаясь к услугам конструкторов базового класса.

Конструктор расширенного класса может напрямую обращаться к конструкторам базового класса посредством ещё одного способа явного вызова конструкторов, предусматривающего применение служебного слова super. Конструкторы расширенных классов могут вызывать друг друга по ключевому слову this().

Если в первой строке тела конструктора производного класса не вызывается ни другой конструктор того же класса, ни конструктор базового класса, компилятор обеспечивает автоматическое обращение к «базовому» конструктору без аргументов – такой вызов осуществляется в первую очередь, прежде всех других

134

исполняемых выражений тела конструктора. Другими словами, первой инструкцией будет super();

Если же в составе базового класса отсутствует конструктор без параметров, в самом начале тела конструктора производного класса обязана явно присутствовать инструкция вызова какоголибо другого конструктора.

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

Конструкторы не относятся к категории методов и не наследуются.

4.2Порядок выполнения конструкторов

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

1.вызов конструктора базового класса;

2.присваивание исходных значений полям объекта посредством выполнения соответствующих выражений и блоков инициализации;

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

Впервую очередь выполняется явное или косвенное обращение

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

135

чего таковой и вызывается. Конструктор базового класса выполняется в соответствии с той же последовательностью стадий – процесс продолжается рекурсивно, завершаясь по достижении конструктора класса Object, поскольку далее не существует конструкторов классов более высокого уровня иерархии. В выражениях, выполняемых в процессе вызова конструктора базового класса, не допускается присутствие ссылок на любые члены текущего объекта.

На второй стадии процесса обрабатываются выражения и блоки инициализации полей в порядке их объявления. В этот момент ссылки на другие члены объекта допустимы, если таковые уже полностью определены.

Наконец, выполняются выражения тела конструктора. Если текущий конструктор был вызван явно, по его завершении управление передаётся в тело конструктора-инициатора, где выполняются оставшиеся инструкции. Процесс повторяется до тех пор, пока управление не будет передано обратно в тело конструктора «исходного» производного класса, название которого было указано в выражении new.

Если во время процесса конструирования выбрасывается исключение, виртуальная машина завершает выполнение оператора new, генерируя то же исключение, и не возвращает ожидаемую ссылку на объект. Поскольку выражение явного вызова конструктора текущего или базового класса должно быть первым в теле конструктораинициатора, отловить исключение, выброшенное вызванным конструктором, невозможно (если бы язык допускал подобное, существовала бы и вероятность созданияобъектов сневернымисходным состоянием).

Пример 24 иллюстрирует различные стадии процесса конструирования объекта производного класса.

Пример 62. Конструктор расширенного класса class X {

protected int xMask = 0x00ff; protected int fullMask;

136

public X() { fullMask = xMask;

}

public int mask (int orig) { return (orig & fullMask);

}

}

class Y extends X {

protected int yMask = 0xff00;

public Y() {

fullMask |= yMask;

}

}

В таблице 17 приведены сведения о содержимом полей, объявленных в классах X и Y, после выполнения каждого этапа процесса создания объекта класса Y.

Таблица 17. Пример инициализации состояния объекта

Этап

Что происходит

xMask

yMask

fullMask

0

Полям присвоены

0

0

0

 

значения по умолчанию

 

 

 

1

Вызван конструктор Y

0

0

0

2

Вызван конструктор X

0

0

0

3

Вызван конструктор класса Object

0

0

0

4

Проинициализированы поля X

0x00ff

0

0

5

Выполнен конструктор X

0x00ff

0

0x00ff

6

Проинициализированы поля Y

0x00ff

0xff00

0x00ff

7

Выполнен конструктор Y

0x00ff

0xff00

0xffff

Если в ходе конструирования объекта вызываются методы, важно представлять себе порядок операций и понимать, что происходит на каждом этапе процесса. В подобной ситуации при вызове метода мы всегда имеем дело с версией этого метода для фактического типа объекта; наличие в полях объекта предусмотренных исходных дан-

137

Соседние файлы в предмете Программирование на Java