В данном параграфе рассматривается ряд нетривиальных ситуаций, связанных с правилами видимости при наследовании.
Поля и методы, помеченные как private (“закрытый, частный”) наследуются, но в классах-наследниках недоступны. Это сделано в целях обеспечения безопасности. Пусть, например, некий класс Password1 обеспечивает проверку правильности пароля, и у него имеется строковое поле password (“пароль”), в котором держится пароль и с которым сравнивается введённый пользователем пароль. Если оно имеет тип public, такое поле общедоступно, и сохранить его в тайне мы не сможем. При отсутствии модификатора видимости или модификаторе protected на первый взгляд имеется необходимое ограничение доступа. Но если мы напишем класс Password2, являющийся наследником от Password1, в нём легко написать метод, “вскрывающий” пароль:
public String getPass(){
return password;
};
Если же поставить модификатор private, то в потомке до прародительского поля password не добраться!
То, что private-поля наследуются, проверить достаточно просто: зададим класс
public class TestPrivate1 {
private String s=”Значение поля private”;
public String get_s(){
return s;
}
}
и его потомок, который просто имеет другое имя, но больше ничего не делает:
public class TestPrivate2 extends TestPrivate1 {
}
Если из объекта, являющегося экземпляром TestPrivate2, вызвать метод get_s(), мы получим строку =”Значение поля private”:
TestPrivate2 tst=new TestPrivate2();
System.out.println(tst.get_s());
Таким образом, поле s наследуется. Но если в классе, где оно задано, не предусмотрен доступ к нему с помощью каких-либо методов, доступных в наследнике, извлечь информацию из этого поля оказывается невозможным.
Модификатор protected предназначен для использования соответствующих полей и методов разработчиками классов-наследников. Он даёт несколько большую открытость, чем пакетный вид доступа (по умолчанию, без модификатора), поскольку в дополнении к видимости из текущего пакета позволяет обеспечить доступ к таким членам в классах-наследниках, находящихся в других пакетах. Модификатором protected полезно помечать различного рода служебные методы, ненужные пользователям класса, но необходимые для функциональности этого класса.
Существует “правило хорошего тона”: поля данных принято помечать модификатором private, а доступ к этим полям обеспечивать с помощью методов с тем же именем, но префиксом get (“получить” - доступ по чтению) и set (“установить” - доступ по записи). Эти методы называют “геттерами” и “сеттерами”. Такие правила основаны на том, что прямой доступ по записи к полям данных может разрушить целостность объекта.
Рассмотрим следующий пример: пусть у нас имеется фигура, отрисовываемая на экране. Изменение её координат должно сопровождаться отрисовкой на новом месте. Но если мы напрямую изменили поле x или y, фигура останется на прежнем месте, хотя поля имеют новые значения! Если же доступ к полю осуществляется через методы setX и setY, кроме изменения значений полей будут вызваны необходимые методы, обеспечивающие перерисовку фигуры в новом месте. Также можно обеспечить проверку вводимых значений на допустимость.
Возможен и гораздо худший случай доступа к полям напрямую: пусть у нас имеется объект-прямоугольник, у которого заданы поля x1,y1- координаты левого верхнего угла,x2,y2- координаты правого нижнего угла,w - ширина, h – высота, s- площадь прямоугольника. Они не являются независимыми: w=x2-x1, h=y2-y1, s=w*h. Поэтому изменение какого-либо из этих полей должно приводить к изменению других. Если же, скажем, изменить только x2, без изменения w и s, части объекта станут несогласованными. Предсказать, как поведёт себя в таких случаях программа, окажется невозможно!
Ещё хуже обстоит дело при наличии наследования в тех случаях, когда в потомке задано поле с тем же именем, что и в прародителе, имеющее совместимый с прародительским полем тип. Так как для полей данных полиморфизм не работает, возможны очень неприятные ошибки.
Указанные выше правила хорошего тона программирования нашли выражение в среде NetBeans при установленном пакете NetBeans Enterprise Pack. В ней при разработке UML-диаграмм добавление в класс поля автоматически приводит к установке ему модификатора private и созданию двух public-методов с тем же именем, но префиксами get и set. Эти типы видимости в дальнейшем, конечно, можно менять, как и удалять ненужные методы.
Иногда возникает необходимость вызвать поле или метод из прародительского класса. Обычно это бывает в случаях, когда в классе-потомке задано поле с таким же именем (но, обычно, другим типом) или переопределён метод. В результате видимость прародительского поля данных или метода в классе-потомке утеряна. Иногда говорят, что поле или метод затеняются в потомке. В этих случаях используют вызов super. имяПоля или super. имяМетода (список параметров). Слово super в этих случаях означает сокращение от superclass. Если метод или поле заданы не в непосредственном прародителе, а унаследованы от более далёкого прародителя, соответствующие вызовы всё равно будут работать. Но комбинации вида super.super. имя не разрешены.
Использовать вызовы с помощью слова super разрешается только для методов и полей данных объектов. Для методов и переменных класса (то есть объявленных с модификатором static) вызовы с помощью ссылки super запрещены.