Кен Арнольд Джеймс Гослинг

Вид материалаДокументы

Содержание


3.4. Переопределение методов и скрытие полей
Перегрузка (overloading)
Переопределение (overriding)
3.4.1. Ключевое слово super
Подобный материал:
1   ...   14   15   16   17   18   19   20   21   ...   81

3.4. Переопределение методов и скрытие полей


В своем новом классе ColorAttr мы переопределили и перегрузили метод valueOf, устанавливающий значение атрибута:
  • Перегрузка (overloading) метода рассматривалась нами раньше; под этим термином понимается создание нескольких методов с одинаковыми именами, но с различными сигнатурами, по которым эти методы отличаются друг от друга.
  • Переопределение (overriding) метода означает, что реализация метода, взятая из суперкласса, заменяется вашей собственной. Сигнатуры методов при этом должны быть идентичными. Обратите внимание: переопределению подлежат только нестатические методы.

В классе ColorAttr мы переопределили метод Attr.valueOf(Object), создав новый метод ColorAttr.valueOf(Object). Этот метод сначала обращается к реализации суперкласса с помощью ключевого слова super и затем вызывает метод decodeColor. Ссылка super может использоваться для вызова методов суперкласса, переопределяемых в данном классе. Позднее мы подробно рассмотрим ссылку super.

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

Переопределенные методы могут иметь собственные значения атрибутов доступа. Расширенный класс может изменить права доступа к методам, унаследованным из суперкласса, но лишь в том случае, если он расширяет их. Метод, объявленный в суперклассе как protected, может быть повторно заявлен как protected (вполне обычная ситуация) или public, но не как private. Ограничивать доступ к методам по сравнению с суперклассом на самом деле было бы бессмысленно, поскольку такое ограничение очень легко обойти: достаточно преобразовать ссылку в супертип с большими правами доступа и использовать ее для вызова метода.

Поля не могут переопределяться; вы можете лишь скрыть их. Если объявить в своем классе поле с тем же именем, что и в суперклассе, то поле суперкласса никуда не исчезнет, однако к нему уже нельзя будет обратиться напрямую, используя одно имя. Для доступа к такому полю нужно будет воспользоваться super или другой ссылкой на тип суперкласса.

При вызове метода для некоторого объекта его реализация выбирается в зависимости от фактического типа объекта. При доступе к полю используется объявленный тип ссылки. Разобраться в этом поможет следующий пример:

class SuperShow {

public String str = “SuperStr”;


public void show() {

System.out.println(“Super.show: ” + str);

}

}


class ExtendShow extends SuperShow {

public String str = “ExtendStr”;

public void show() {

System.out.println(“Extend.show: ” + str);

}

public static void main(String[] args) {

ExtendShow ext = new ExtendShow();

SuperShow sup = ext;

sup.show();

ext.show();

System.out.println(“sup.str = ” + sup.str);

System.out.println(“ext.str = ” + ext.str);

}

}


У нас имеется всего один объект, но на него указывают две ссылки — тип одной из них совпадает с типом объекта, а другая объявлена как ссылка на суперкласс. Вот как выглядят результаты работы данного примера:

Extend.show: ExtendStr

Extend.show: ExtendStr

sup.str = SuperStr

ext.str = ExtendStr


Метод show ведет себя именно так, как следовало ожидать: вызываемый метод зависит от фактического типа объекта, а не от типа ссылки. Когда мы имеем дело с объектом ExtendShow, вызывается метод именно этого класса, даже если доступ к нему осуществляется через ссылку на объект типа SuperShow.

Что касается поля str, то выбор класса, которому принадлежит это поле, осуществляется на основании объявленного типа ссылки, а не фактического типа объекта. В сущности, каждый объект класса ExtendShow содержит два поля типа String, каждое из которых называется str; одно из них наследуется от суперкласса и скрывается другим, собственным полем класса Extend Show:



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

Если существующий метод получает параметр типа SuperShow и обращается к str через ссылку на объект-параметр, он всегда будет получать Super Show.str, даже если методу на самом деле был передан объект типа Extend Show. Если бы классы были спроектированы так, чтобы для доступа к строке применялся специальный метод, то в этом случае был бы вызван переопределенный метод, возвращающий ExtendShow.str. Это еще одна из причин, по которой определение классов с закрытыми данными, доступ к которым осуществляется с помощью методов, оказывается предпочтительным.

Скрытие полей разрешено в Java для того, чтобы при новой реализации существующих суперклассов можно было включить в них новые поля с атрибутами public или protected, не нарушая при этом работы подкласса. Если бы использование одинаковых имен в суперклассе и подклассе было запрещено, то включение нового поля в существующий суперкласс потенциально могло бы привести к конфликтам с подклассами, в которых это имя уже используется. В таком случае запрет на добавление новых полей к существующим суперклассам связывал бы руки программистам, которые не могли бы дополнить суперклассы новыми полями с атрибутами public или protected. Формально можно было бы возразить, что классы должны содержать только данные private, но Java поддерживает обе возможности.

3.4.1. Ключевое слово super


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

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

class That {

/** вернуть имя класса */

protected String nm() {

return “That”;

}

}


class More extends That {

protected String nm() {

return “More”;

}


protected void printNM() {

That sref = super;


System.out.println(“this.nm() = ” + this.nm());

System.out.println(“sref.nm() = ” + sref.nm());

System.out.println(“super.nm() = ” + super.nm());

}

}

А вот как выглядит результат работы printNM:

this.nm() = More

sref.nm() = More

super.nm() = That


Ключевое слово super может также применяться для доступа к защищенным членам суперкласса.