Кен Арнольд Джеймс Гослинг
Вид материала | Документы |
СодержаниеГлава 4ИНТЕРФЕЙСЫ 4.1. Пример интерфейса 4.2. Одиночное и множественное наследование |
- Джеймс трефил, 41001.36kb.
- Джеймс А. Дискретная математика и комбинаторика [Текст] / Джеймс А. Андерсон, 42.79kb.
- Человеческая способность эти ценности производить и использовать; является важнейшей, 110.76kb.
- Джеймс блиш города в полете 1-4 триумф времени вернись домой, землянин жизнь ради звезд, 10495.38kb.
- Джеймс Н. Фрей. Как написать гениальный роман, 2872.12kb.
- Дп «авто интернешнл» Київ, вул. Урицького, 1а Тел. (044) 20-60-333 Факс. (044) 20-60-343, 82.44kb.
- Тема Кол-во страниц, 26.85kb.
- Тема Кол-во страниц, 56.3kb.
- Тема Кол-во страниц, 20.7kb.
- Арнольд И. В. Стилистика современного английского языка, 20.42kb.
Глава 4
ИНТЕРФЕЙСЫ
“Дирижирование” — это когда вы рисуете
свои “проекты” прямо в воздухе, палочкой или руками,
и нарисованное становится “инструкциями” для парней в галстуках, которые в данный момент предпочли бы
оказаться где-нибудь на рыбалке.
Фрэнк Заппа
Основной единицей проектирования в Java являются открытые (public) методы, которые могут вызываться для объектов. Интерфейсы предназначены для объявления типов, состоящих только из абстрактных методов и констант; они позволяют задать для этих методов произвольную реализацию. Интерфейс является выражением чистой концепции проектирования, тогда как класс представляет собой смесь проектирования и конкретной реализации.
Методы, входящие в интерфейс, могут быть реализованы в классе так, как сочтет нужным проектировщик класса. Следовательно, интерфейсы имеют значительно больше возможностей реализации, нежели классы.
4.1. Пример интерфейса
В предыдущей главе мы представили читателю класс Attr и показали, как расширить его для создания специализированных типов объектов с атрибутами. Теперь все, что нам нужно, — научиться связывать атрибуты с объектами. Для этого служат два подхода: композиция и наследование. Вы можете создать в объекте набор определенных атрибутов и предоставить программисту доступ к этому набору. Второй метод состоит в том, что вы рассматриваете атрибуты объекта как составную часть его типа и включаете их в иерархию класса. Оба подхода вполне допустимы; мы полагаем, что хранение атрибутов в иерархии класса приносит больше пользы. Мы создадим тип Attributed, который может использоваться для наделения объектов атрибутами посредством закрепления за ними объектов Attr.
Однако в Java поддерживается только одиночное наследование (single inheritance) при реализации — это означает, что новый класс может являться непосредственным расширением всего одного класса. Если вы создаете класс Attributed, от которого порождаются другие классы, то вам либо придется закладывать Attributed в основу всей иерархии, либо программисты окажутся перед выбором: расширять ли им класс Attributed или какой-нибудь другой полезный класс.
Каждый раз, когда вы создаете полезное средство вроде Attributed, возникает желание включить его в корневой класс Object. Если бы это разрешалось, то класс Object вскоре разросся бы настолько, что работать с ним стало бы невозможно.
В Java допускается множественное наследование интерфейсов, так что вместо того, чтобы включать возможности класса Attributed в Object, мы оформим его в виде интерфейса. Например, чтобы наделить атрибутами наш класс небесных тел, можно объявить его следующим образом:
class AttributedBody extends Body
implements Attributed
Разумеется, для этого нам понадобится интерфейс Attributed:
interface Attributed {
void add(Attr newAttr);
Attr find(String attrName);
Attr remove(String attrName);
java.util.Enumeration attrs();
}
В данном интерфейсе объявляются четыре метода. Первый из них добавляет новый атрибут в объект Attributed; второй проверяет, включался ли ранее в объект атрибут с указанным именем; третий удаляет атрибут из объекта; четвертый возвращает список атрибутов, закрепленных за объектом. В последнем из них используется интерфейс Enumeration, определенный для классов-коллекций Java. java.util.enumeration подробно рассматривается в главе 12.
Все методы, входящие в интерфейс, неявно объявляются абстрактными; так как интерфейс не может содержать собственной реализации объявленных в нем методов. Поэтому нет необходимости объявлять их с ключевым словом abstract. Каждый класс, реализующий интерфейс, должен реализовать все его методы; если же в классе реализуется только некоторая часть методов интерфейса, такой класс (в обязательном порядке) объявляется abstract.
Методы интерфейса всегда являются открытыми. Они не могут быть статическими, поскольку статические методы всегда относятся к конкретному классу и никогда не бывают абстрактными, а интерфейс может включать только абстрактные методъ.
С другой стороны, поля интерфейса всегда объявляются static и final. Они представляют собой константы, используемые при вызове методов. Например, интерфейс, в контракте которого предусмотрено несколько уровней точности, может выглядеть следующим образом:
interface Verbose {
int SILENT = 0;
int TERSE = 1;
int NORMAL = 2;
int VERBOSE = 3;
void setVerbosity(int level);
int getVerbosity();
}
Константы SILENT, TERSE, NORMAL и VERBOSE передаются методу set Verbosity; таким образом можно присвоить имена постоянным величинам, имеющим конкретное значение. Они должны быть константами, а все поля интерфейса неявно объявляются static и final.
4.2. Одиночное и множественное наследование
В языке Java новый класс может расширять всего один суперкласс — такая модель носит название одиночного наследования. Расширение класса означает, что новый класс наследует от своего суперкласса не только контракт, но и реализацию. В некоторых объектно-ориентированных языках используется множественное наследование, при котором новый класс может иметь два и более суперклассов.
Множественное наследование оказывается полезным в тех случаях, когда требуется наделить класс новыми возможностями и при этом сохранить большую часть (или все) старых свойств. Однако при наличии нескольких суперклассов возникают проблемы, связанные с двойственным наследованием. Например, рассмотрим следующую иерархию типов:
Обычно такая ситуация называется “ромбовидным наследованием”, и в ней нет ничего плохого — подобная структура встречается довольно часто. Проблема заключается в наследовании реализации. Если класс W содержит открытое поле goggin и у вас имеется ссылка на объект типа Z с именем zref, то чему будет соответствовать ссылка zref.goggin? Будет ли она представлять собой копию goggin из класса X, или из класса Y, или же X и Y будут использовать одну копию goggin, поскольку в действительности W входит в Z всего один раз, хотя Z одновременно является и X, и Y?
Чтобы избежать подобных проблем, в Java используется объектно-ориентированная модель с одиночным наследованием.
Одиночное наследование способствует правильному проектированию. Проблемы множественного наследования возникают из расширения классов при их реализации. Поэтому Java предоставляет возможность наследования контракта без связанной с ним реализации. Для этого вместо типа class используется тип interface.
Таким образом, интерфейсы входят в иерархию классов и наделяют Java возможностями множественного наследования.
Классы, расширяемые данным классом, и реализованные им интерфейсы совместно называются его супертипами; с точки зрения супертипов, новый класс является подтипом. В понятие “полного типа” нового класса входят все его супертипы, поэтому ссылка на объект класса может использоваться полиморфно — то есть всюду, где должна находиться ссылка на объект любого из супертипов (класса или интерфейса). Определения интерфейсов создают имена типов, подобно тому как это происходит с именами классов; вы можете использовать имя интерфейса в качестве имени переменной и присвоить ей любой объект, реализующий данный интерфейс.