}
Переменные 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
Несмотря на то, что к полю класса идут обращения через разные классы, переменная всего одна.
Итак, наследники могут объявлять поля с именами, совпадающими с родительскими полями. Такие объявления называют скрывающими. При этом объекты будут содержать оба значения, а компилятор будет каждый раз определять, с каким из них надо работать.
Методы
Рассмотрим случай переопределения (overriding) методов:
class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } }
И строки, демонстрирующие работу с этими методами:
Child c = new Child(); System.out.println(c.getValue()); Parent p = c; System.out.println(p.getValue());
Результатом будет: