Главная » Java » Конструкторы в расширенных классах

0

При расширении класса необходимо выбрать один из конструкторов суперкласса и вызывать его при конструировании  объектов нового класса. Это необходимо для правильного создания части объекта, относящейся к суперклассу, помимо установки правильного исходного состояния для всех добавленных полей.

Конструктор суперкласса может вызываться в конструкторе подкласса посредством явного вызова super(). Примером может служить первый из конструкторов приведенного выше класса ColorAttr. Если вызов конструктора суперкласса не является самым первым выполняемым оператором в конструкторе нового класса, то перед выполнением последнего автоматически вызывается безаргументный конструктор суперкласса. Если же суперкласс не имеет безаргументного конструктора, вы должны явно вызвать конструктор суперкласса с параметрами. Вызов super() непременно должен быть первым оператором нового конструктора.

Вызов конструктора суперкласса демонстрируется на примере первого конструктора ColorAttr. Сначала полученные имя и значение передаются конструктору суперкласса, получающему два аргумента. Затем конструктор вызывает свой собственный метод decodeColor для того, чтобы поле myColor ссылалось на нужный цветовой объект.

Вы можете временно отложить вызов конструктора суперкласса и использовать вместо него один из конструкторов того же класса — в этом случае вместо super() используется this(). Второй конструктор ColorAttr поступает именно так. Это было сделано для того, чтобы каждому цветовому атрибуту заведомо был присвоен какой-то цвет; если он не указан, то по умолчанию присваивается цвет “transparent” (то есть “прозрачный”).

Третий конструктор ColorAttr позволяет программисту при создании объекта ColorAttr сразу же указать объект ScreenColor. Первые два конструктора преобразуют свои параметры в объекты ScreenColor при помощи метода decodeColor, вызов которого сопряжен с накладными расходами. Если программист уже располагает объектом ScreenColor, который будет использован в качестве цветового значения, было бы желательно избежать затрат на излишнее преобразование. В этом примере конструктор предназначен для повышения эффективности, а не для добавления новых возможностей.

Сигнатуры конструкторов класса ColorAttr в точности совпадают с сигнатурами конструкторов суперкласса, но это ни в коем случае не является обязательным. Иногда бывает удобно сделать так, чтобы часть или все конструкторы расширенного класса передавали нужные параметры в конструкторы суперкласса и обходились без своих

параметров (или число таких параметров было бы минимальным). Нередки случаи, когда сигнатуры конструкторов расширенного класса не имеют ничего общего с сигнатурами конструкторов суперкласса.

Язык Java может создать безаргументный  конструктор по умолчанию. Работа такого конструктора для расширяемого класса начинается с вызова безаргументного конструктора суперкласса. Однако, если в суперклассе отсутствует безаргументный конструктор, расширенный класс должен содержать хотя бы один конструктор. Конструктор расширенного класса по умолчанию эквивалентен следующему:

public class ExtendedClass extends SimpleClass {

public ExtendedClass() {

super();

}

}

Помните, что доступность конструктора определяется доступностью класса. Так как

ExtendedClass объявлен как public, конструктор по умолчанию также будет public.

3.3.1. Порядок вызова конструкторов

При создании объекта всем его полям присваиваются исходные значения по умолчанию в зависимости от их типа (ноль для всех числовых типов, ‘\u0000’ для char, false для boolean и null для ссылок на объекты). Затем происходит вызов конструктора. Каждый конструктор выполняется за три фазы:

1. Вызов конструктора суперкласса.

2. Присвоение значений полям при помощи инициализаторов.

3. Выполнение тела конструктора.

Приведем пример, который позволит нам проследить за этой процедурой:

class X {

protected int xMask = 0x00ff;

protected int fullMask;

public X() {

fullMask = xMask;

}

public int mask(int orig) {

return (orig & fullMask);

}

}

class Y extends X {

protected int yMask = 0xff00;

public Y() {

fullMask |= yMask;

}

}

Если создать объект типа Y и проследить за его конструированием  шаг за шагом, то значения полей будут меняться следующим образом:


Шаг

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

xMask

yMask

fullMask

0

Присвоение полям значений по умолчанию

0

0

0

1

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

0

0

0

2

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

0

0

0

3

Инициализация полей X

0x00ff

0

0

4

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

0x00ff

0

0x00ff

5

Инициализация полей Y

0x00ff

0xff00

0x00ff

6

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

0x00ff

0xff00

0xffff

Этот порядок имеет ряд важных следствий для вызова методов во время конструирования.  Обращаясь к методу, вы всегда имеете дело с его реализацией для конкретного объекта; поля, используемые в нем, могут быть еще не инициализированы. Если на приведенном выше шаге 4 конструктор X вызовет метод mask, то маска будет иметь значение 0x00ff, а не 0xffff, несмотря на то что в более позднем вызове mask (после завершения конструирования  объекта) будет использовано значение 0xffff.

Кроме того, представьте себе ситуацию, в которой класс Y переопределяет реализацию mask так, чтобы в вычислениях явно использовалось  поле yMask. Если конструктор X использует метод mask, на самом деле будет вызван mask класса Y, а в этот момент значение yMask равно нулю вместо ожидаемого 0xff00.

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

Упражнение 3.2

Наберите код приведенных выше классов X и Y и включите в него операторы вывода для наблюдения за значениями масок. Напишите метод main и запустите его, чтобы ознакомиться с результатами. Переопределите mask в классе Y и снова выполните тестирование.

Упражнение 3.3

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

Источник: Арнольд К., Гослинг Д. – Язык программирования Java (1997)

По теме:

  • Комментарии