Программирование на Java

       

Поля


Начнем с полей, которые могут быть статическими или динамическими. Рассмотрим пример:

class Parent { int a=2; } class Child extends Parent { int a=3; }

Прежде всего, нужно сказать, что такое объявление корректно. Наследники могут объявлять поля с любыми именами, даже совпадающими с родительскими. Затем, необходимо понять, как два одноименных поля будут сосуществовать. Действительно, объекты класса Child будут содержать сразу две переменных, а поскольку они могут отличаться не только значением, но и типом (ведь это два независимых поля), именно компилятор будет определять, какое из значений использовать. Компилятор может опираться только на тип ссылки, с помощью которой происходит обращение к полю:

Child c = new Child(); System.out.println(c.a); Parent p = c; System.out.println(p.a);

Обе ссылки указывают на один и тот же объект, порожденный от класса Child, но одна из них имеет такой же тип, а другая – Parent. Отсюда следуют и результаты:

3 2

Объявление поля в классе-наследнике "скрыло" родительское поле. Данное объявление так и называется – "скрывающим" (hiding). Это особый случай перекрытия областей видимости, отличный от "затеняющего" (shadowing) и "заслоняющего" (obscuring) объявлений. Тем не менее, родительское поле продолжает существовать. К нему можно обратиться и явно:

class Child extends Parent { int a=3; // скрывающее объявление int b=((Parent)this).a; // более громоздкое объявление int c=super.a; // более простое }

Переменные b и c получат значение, хранящееся в родительском поле a. Хотя выражение с super более простое, оно не позволит обратиться на два уровня вверх по дереву наследования. А ведь вполне возможно, что в родительском классе это поле также было скрывающим и в родителе родителя хранится еще одно значение. К нему можно обратиться явным приведением, как это делается для b.

Рассмотрим следующий пример:

class Parent { int x=0; public void printX() { System.out.println(x); } } class Child extends Parent { int x=-1; }


Каков будет результат следующих строк?

new Child().printX();



Значение какого поля будет распечатано? Метод вызывается с помощью ссылки типа Child, но это не сыграет никакой роли. Вызывается метод, определенный в классе Parent, и компилятор, конечно, расценил обращение к полю x в этом методе именно как к полю класса Parent. Поэтому результатом будет 0.

Перейдем к статическим полям. На самом деле, для них проблем и конфликтов, связанных с полиморфизмом, не существует.

Рассмотрим пример:

class Parent { static int a=2; } class Child extends Parent { static int a=3; }

Каков будет результат следующих строк?

Child c = new Child(); System.out.println(c.a); Parent p = c; System.out.println(p.a);

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

Поэтому рассматриваемый пример эквивалентен:

System.out.println(Child.a) System.out.println(Parent.a)

А его результат сомнений уже не вызывает:

3 2

Можно привести следующее пояснение. Статическое поле принадлежит классу, а не объекту. В результате появление классов-наследников со скрывающими (hiding) объявлениями никак не сказывается на работе с исходным полем. Компилятор всегда может определить, через ссылку какого типа происходит обращение к нему.

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

class Parent { static int a; }

class Child extends Parent { }

Каков будет результат следующих строк?

Child.a=10; Parent.a=5; System.out.println(Child.a);

В этом примере поле a не было скрыто и передалось по наследству классу Child. Однако результат показывает, что это все же одно поле:

5

Несмотря на то, что к полю класса идут обращения через разные классы, переменная всего одна.

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


Содержание раздела