Java — Смысл интерфейсов.


Содержание

Занятие 4

Интерфейсы

Абстрактные классы

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

Например, классы Point (точка), Circle (круг) и Rectangle (прямоугольник) унаследованы от класса Figure (фигура). В классе Figure есть метод paint() , общий для всех подклассов — он нужен, чтобы нарисовать фигуру. Но между рисованием круга, точки и прямоугольника нет ничего общего, поэтому бессмысленно программировать этот метод в классе Figure .

В таких случаях после заголовка метода и списка параметров вместо фигурных скобок ставится точка с запятой, а перед объявлением метода указывается ключевое слово abstract . Такой метод называется абстрактным, т.е. лишенным реализации.

Класс, в котором есть хотя бы один абстрактный метод, называется абстрактным классом и перед словом class должно также стоять слово abstract .

Создать объект абстрактного класса нельзя. Можно только унаследовать от этого класса другие классы, переопределить в них абстрактные методы (наполнив их конкретным содержимым) и создавать объекты уже этих классов. *

Множественное наследование

Множественным наследованием называется ситуация, когда класс наследует от двух или более классов.

Пусть, например, уже разработаны классы Clock (часы) и Phone (телефон) и возникает необходимость написать новый класс Cellurar (сотовый телефон), который совмещает в себе структуру и поведение обоих этих классов (его можно использовать как телефон, но он обладает также всей функциональностью часов). Очень удобно просто унаследовать класс Cellular от классов Clock и Phone . Во-первых, программисту не приходится заново переписывать многочисленные методы. Во-вторых, сохраняются все преимущества полиморфизма: объект класса Cellular можно использовать в программе и в качестве часов (как объект класса Clock ) и в качестве телефона (как объект класса Phone ).

Однако множественное наследование потенциально способно породить трудноразрешимые проблемы. Они начинаются, когда у классов-предков есть методы с одинаковыми сигнатурами (т.е. именем и числом параметров).

Например, в классе Clock есть метод ring() , который вызывается, когда срабатывает таймер будильника. Но в классе Phone тоже есть метод ring() , который вызывается, когда кто-то звонит по телефону и надо оповестить об этом владельца. Когда класс Cellular наследует от классов Clock и Phone , он получает метод ring() . Но какой из его вариантов? *

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

Java множественное наследование не поддерживает.

Заметим, однако, что если метод ring() хотя бы в одном из классов Clock и Phone является абстрактным, то конфликта возникнуть не может. Абстрактный метод не имеет реализации, а следовательно «побеждает» тот метод, который абстрактным не является. Если же метод является абстрактным в обоих классах, он останется таким же и в их потомке.

Используя эту особенность абстрактных классов, авторы языка Java ввели в него особенную конструкцию — интерфейсы, позволяющие сохранить часть преимуществ множественного наследования (а именно преимущества полиморфизма), избежав при этом главной проблемы множественного наследования.

Понятие интерфейса в Java. Описание интерфейса

представляет собой класс, в котором все поля — константы (т.е. статические — static и неизменяемые — final ), а все методы абстрактные.

При описании интерфейса вместо ключевого слова class используется ключевое слово interface , после которого указывается имя интерфейса, а затем, в фигурных скобках список полей-констант и методов. Никаких модификаторов перед объявлением полей и методов ставить не надо: все поля автоматически становятся public static final , а методы — public abstract . Методы не могут иметь реализации, т.е. после закрывающей круглой скобки сразу ставится точка с запятой.

Опишем, например, интерфейс для объекта, который «умеет» сообщать информацию о себе в формате прайс-листа (т.е. сообщать свое название, цену, и краткое описание).

Для разнообразия метод getPrice() в этом примере требует один целочисленный параметр (количество единиц товара).

Такой интерфейс полезен для программы типа Интернет-магазин, которая должна по запросу пользователя формировать прайс — перечень товаров с указанием их цены и описания.

Реализация интерфейса

Класс может реализовывать интерфейс, т.е. перенимать его поведение. Эта процедура аналогична наследованию, только вместо ключевого слова extends используется ключевое слово implements . Но если после extends может быть указан только один класс, то после implements можно перечислить через запятую произвольное количество интерфейсов. Класс может одновременно наследовать от другого класса и реализовывать интерфейсы.

Мы можем взять любой существующий в программе класс и «научить» его сообщать о себе все необходимые сведения. Например, класс Dog , с которым мы работали на прошлом занятии. Можно изменить этот класс, заставив его реализовывать наш интерфейс, а можно оставить в неприкосновенности * и создать на его базе новый класс. В новом классе обязательно требуется переопределить абстрактные методы интерфейса.

Класс Dog2 «умеет» то же самое, что и старый класс Dog , но помимо этого его можно использовать в программе Интернет-магазина для формирования прайса. Обратите внимание, класс Dog ничего не знал о цене, поэтому понадобилось добавить метод setPrice() и поле price , чтобы эту цену можно было бы изменять. А изменять описание собаки не понадобится, поэтому метод getDescription() просто выводит одну и ту же сроку.

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

Переменные интерфейсного типа

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

PriceItem pi; // переменная интерфейсного типа Dog2 dog = new Dog2(); // создается объект класса Dog2, на него ссылается переменная dog dog.voice(); // можно вызвать метод лая System.out.println(dog.getTitle()); // можно вывести название товара Dog oldDog = dog; // переменная oldDog ссылается на тот же самый объект oldDog.voice(); // можно работать с объектом нового класса по-старому pi = dog; // переменная pi рассматривает тот же самый объект как товар для прайса pi.voice(); // НЕ ПОЛУЧИТСЯ. Этого метода нет в интерфейсе PriceItem *

Мы можем поместить собак, велосипеды и компьютеры в один массив goods (товары) и в цикле сформировать прайс:

PriceItem[] goods; . // создание и заполнение массива элементами, // поддерживающими интерфейс PriceItem for ( int i = 0; i System.out.println( «Название: » + goods[i].getTitle() + «, цена за единицу товара: » + goods[i].getPrice(1) + «, описание: » + goods[i].getDescription() + «.» ); >

Используя интерфейс, программист получает возможность задать описание того, «что класс делает», не заботясь о деталях, «как он это делает». Каждый класс, реализующий интерфейс, сам может выбирать способы реализации.

Приемы программирования: пример применения интерфейсов

Предположим, в нашей программе есть объект, который следит за текущим временем и каждую минуту (когда время меняется) «сообщает» об этом другим объектам. Это очень логичный и удобный способ организации программы, в которой объекты должны что-то делать при наступлении определенного времени (или события). Гораздо лучше, чем учить каждый объект следить за временем.

Один объект может «сообщить» что-то другому объекту, вызвав его метод. Пусть информацию о времени обрабатывает метод sayTime(int hours, int minutes) . Для того, чтобы вызвать этот метод у какого-то объекта, надо быть уверенным, что такой метод описан в классе этого объекта. Можно определить интерфейс, скажем TimeListener , и реализовать его во всех классах, которым нужно следить за временем, не вмешиваясь в основную иерархию этих классов. И тогда у нас может быть разновидность умной собаки, которая лает ровно в полночь и разновидность кнопки, которая может автоматически срабатывать через заданный промежуток времени.

Класс, следящий за временем будет иметь внутренний список объектов типа TimeListener , методы для добавления и удаления объектов в этот список (те объекты, которые «захотят» следить за временем, вызовут этот метод) и каждую минуту объект этого класса (достаточно одного такого объекта на всю программу) будет вызывать метод sayTime(int hours, int minutes) для каждого из объектов в этом списке.

Пакеты и области видимости


Пакеты

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

Каждый пакет имеет имя. Имя представляет собой обычный идентификатор Java. Особенность заключается в том, что это имя одновременно является названием папки, в которой хранятся файлы классов, входящие в пакет. А точка в имени преобразуется в разделитель имен файловой системы. То есть пакет с именем java.util будет представлен папкой util , находящейся внутри папки java .

В папке хранятся файлы с расширением .java , содержащие описания классов, входящих в пакет. В начале такого файла должен стоять оператор package , после которого записывается имя пакета.

Импортирование пакетов

Полное имя класса состоит из идентификатора, указанного после ключевого слова class и предшествующего ему имени пакета, в котором этот класс находится. Классы ClassA и ClassB , описанные в пакете package1 , имеют полные имена package1.ClassA и package1.ClassB .

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

>new ClassB(); varb.f();

package1. >new package1.ClassB(); varb.f();

Если классы находятся в разных пакетах, для обращения друг к другу они должны пользоваться полными именами.

Это ограничение можно обойти, если импортировать нужный класс из другого пакета.

Для импортирования класса используется ключевое слово import , после которого указывается его полное имя. Например, можно импортировать класс Vector из пакета java.util :

Теперь можно пользоваться именем Vector вместо java.util.Vector .

Чтобы импортировать пакет полностью (то есть, ко всем классам пакета можно будет обращаться с помощью сокращенного имени), после имени пакета ставится точка и звездочка. Так, команда

импортирует все файлы из пакета java.util . Но таким способом пользоваться не рекомендуется, так как при этом из разных пакетов могут импортироваться файлы с одинаковыми именами. *

Eclipse позволяет облегчить жизнь разработчику. Если в программе используется класс с неизвестным именем, на полях редактора кода появляется значок предупреждения об ошибке. Щелчок по этому значку выводит варианты решения проблемы. Например, создать новый класс. Или импортировать существующий (при этом выводится список всех доступных пакетов, содержащих класс с таким именем). Если выбрать вариант «Import» соответствующая директива import будет автоматически добавлена в начало пакета.

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

Цукерберг рекомендует:  Оптимизация конфигурации MySQL

Поэтому при импортировании нескольких пакетов, в которых содержатся классы с одинаковыми именами, может возникнуть путаница в этих именах. Однако Java справляется с этой проблемой, «отдавая» сокращенное имя первому из импортированных классов. К другому классу можно обратиться с помощью его полного имени.

Если же совпадут и имена пакетов и имена классов, проблема окажется неразрешимой. А поскольку программист может пользоваться множеством пакетов разных разработчиков, такая ситуация вполне вероятна, если разработчики не договорятся. Поэтому существует соглашение об именовании пакетов (особенно тех, которые впоследствии могут распространяться).

Рекомендуется для названия пакета использовать адрес сайта фирмы-разработчика. Адреса сайтов есть практически у всех серьезных разработчиков программ (и, что самое главное, адреса сайтов не могут совпадать). Адрес сайта рекомендуется записывать наоборот. То есть, если адрес — sun.com, то имя пакета должно начинаться с com.sun. Кстати, таких пакетов довольно много в вашей системе, их поставляет фирма Sun Microsystems, разработчик языка Java.

Файловая структура Java-проекта

Итак, Java-проект может состоять из нескольких пакетов. Каждому пакету в файловой структуре операционной системы соответствует одна папка.

В пакете могут содержаться классы и интерфейсы. Они хранятся в файлах с расширением .java.

В каждом файле с расширением .java обязательно описывается один класс или интерфейс, название которого совпадает с названием этого файла. Он должен быть объявлен как открытый (перед объявлением класса указывается ключевое слово public ). Все остальные классы и интерфейсы из этого файла являются закрытыми — их нельзя использовать за пределами этого пакета (ни с помощью длинного, ни с помощью короткого имени). Поэтому с классом Dog , который мы разрабатывали на прошлом занятии, можно работать исключительно в пределах его пакета.

Все классы и интерфейсы, которые впоследствии предполагается использовать в других пакетах, должны быть объявлены открытыми (а значит, находиться в отдельных файлах).

Eclipse создает новый файл с расширением .java автоматически, если выполнить команду New Class или New Interface .

Файл с расширением .java — это обычный текстовый файл. Его можно открывать и редактировать как с помощью Eclipse, так и в любом другом текстовом редакторе (даже в Блокноте).

Для каждого класса (открытого или закрытого) Java создает файл с расширением .class . Это двоичный файл, в котором хранятся команды на внутреннем языке Java. Эти файлы недоступны для редактирования в Eclipse (если попытаться их открыть, Eclipse на самом деле откроет соответствующий .java -файл). Чтобы они не мешали, их можно скрыть с помощью фильтра. Для этого в представлении Navigator нажмите маленькую треугольную кнопку справа ( menu ) и выберите команду Filters. В открывшемся окне поставьте галочку напротив расширения .class , чтобы скрыть из панели Navigator соответствующие файлы.

Области видимости классов

Область видимости класса — это участок программы, в котором можно объявлять переменные и создавать объекты этого класса.

Объект, созданный в области видимости своего класса, может продолжать свое существование и за ее пределами. Но переменные типа «невидимого» класса объявить нельзя, поэтому обращаться к такому объекту можно только с помощью переменных типов «видимых» классов-предков. Следовательно, и вызывать можно лишь те методы объекта, которые его класс унаследовал от соответствующего предка.

Обычный класс всегда виден в пределах своего пакета. Если класс является открытым, он виден также из других пакетов (в этом случае к нему надо обращаться, используя полное имя или, если предварительно импортировать пакет, сокращенное).

Вложенные классы, объявленные без модификатора public , видны только в методах содержащего их класса. Классы, описанные в методах, видны только в пределах этих методов.

Анонимные классы видны лишь в пределах команды, которой они создаются.

Области видимости членов класса

Члены класса (методы и атрибуты), объявленные как public , видны везде, где виден сам класс.

Члены класса, объявленные как protected видны в самом классе и его потомках.

Члены класса, объявленные как private , видны только в пределах класса.

Если к члену класса не применяется ни один из модификаторов public , private , protected , он виден в пределах текущего пакета.


«Видимость» поля означает, что его можно использовать в выражениях, передавать в качестве аргумента в методы, изменять его значение с помощью присваивания.

«Видимость» метода означает возможность его вызова.

Для обращения к полям и методам класса используется полное имя, состоящее из имени объекта соответствующего класса и собственно имени атрибута или метода. В теле метода того же класса имя объекта можно опускать (если подразумевается this — объект, для которого вызван данный метод).

Области видимости переменных

Переменные, объявленные в теле метода, видны от места объявления до конца блока, в котором это объявление находится. Границы блока задаются фигурными скобками <> . Поэтому в следующем примере:

используются две разные переменные x (первая переменная, равная 0, перестает существовать за границами своего блока).

Переменные, являющимися параметрами метода, видны во всем теле метода.

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

Нельзя, в частности, объявлять в теле метода переменную, совпадающую (по имени) с одним из параметров метода.

Конфликты имен

Конфликт имен возникает, если в одной области видимости оказываются два объекта с одинаковыми именами, причем такая ситуация является в языке разрешенной (в противном случае это не конфликт имен, а ошибка в программе).

Конфликт имен возникает, когда импортируются два пакета, содержащие классы с одинаковыми именами.

В этом случае Java отдает предпочтение классу, который был импортирован первым. Для обращения ко остальным классам следует использовать их полные имена.

Java «просматривает» имена классов в следующем порядке. Сначала — классы, импортированные поодиночке. Потом — классы, определенные в данном пакете. В последнюю очередь классы из пакетов, импортируемых полностью в порядке следования команд import .

Конфликт имен возникает так же, когда имя одного из параметров метода совпадает с именем одного из атрибутов этого же класса. Такая ситуация возникает довольно часто, поскольку для метода, изменяющего значение этого атрибута, такое называние параметра довольно наглядно. Например:

Такой заголовок метода setAge(int age) лучше, чем использовавшийся нами на прошлом занятии setAge(int a) , поскольку сразу позволяет судить о назначении параметра. Однако возникает вопрос: к чему будет относиться имя age в теле этого метода — к атрибуту или к параметру.

Ответ: к параметру. Имя параметра «перекрывает» имя атрибута.

Для того, чтобы обратиться к атрибуту, следует использовать полное имя (т.е. указатель на объект this ).

Реализация метода должна выглядеть следующим образом:

Дополнительная литература

1. Вязовик Н.А. Программирование на Java. (глава 8)

2. Хабибуллин И.Ш. Самоучитель Java 2. (глава 3)

Интерфейсы interface

В статье рассматривается одно из свойств Java, как языка объектно-ориентированного программирования, вопрос использования интерфейса. Статья в большей степени ориентирована на философию Java, чем на практические рекомендации по программированию.

Первый вопрос, возникающий при знакомстве с Java, — Зачем нужны интерфейсы? Нельзя ли обойтись абстрактными классами? Может быть для маскировки отсутствия множественного наследования?

Привычные варианты ответа: класс вводит новый тип, класс обобщает и т.д.

Декларация типа

В Java новый тип можно определить спецификацией интерфейса. Любой java интерфейс (interface) может иметь много реализаций. Любой класс может реализовывать несколько интерфейсов.

В качестве примера создадим интерфейс INumber — тип, описывающий функции использования числовых значений. Ограничимся только операциями сложения и умножения.

Реализация интерфейса, implements

Пример реализации интерфейса INumber при описании класса целочисленных значений :

В классе IntNumber реализованы (переопределены) методы интерфейса с использованием аннотации Override.

Пример использования интерфейса INumber :

В результате выполнения примера «TestINumber» в консоль будет выведен следующий текст :

Из приведенного примера видно :

  • Интерфейс позволяет объявить тип. В приведенном примере объявляются переменные и параметры типа INumber, описываются действия над ними. Компиляция выполняется без ошибок.
  • Реализация типа передается через объект. Объекты n1, n2 и n3 передаются в метод calculation через параметры. Таким образом компилятор информируется, что объекты проинициализированы где-то за пределами данного модуля. Этого достаточно и классы пока не нужны.

Добавим вариант реализации интерфейса при создании класса вещественного числа :


Протестируем классы, реализующие интерфейс INumber :

Результат выполнения тестовой программы:

Следует обратить внимание, что реализация передается через объект. Класс нужен для порождения объекта, несущего реализацию. Но не обязательно, как увидим позднее. Интересно отметить, что результат операции над INumber зависит от последовательности использования переменных. Эффект возникает потому, что в спецификации типа мы опустили важные для чисел свойства: точность и диапазон допустимых значений. В результате они неявно берутся из базового типа, использованного при реализации.

Реализация интерфейса

В предыдущем примере мы видели, что реализация передается через объект. Следовательно, в объекте упакована вся необходимая информация по реализации интерфейса. Если поведение определяется интерфейсом, а реализация упакована в объекте, то зачем нужен класс? — Классы нужны для наследования реализации и повторного использования кода. Если повторное использование объекта не требуется, то и описание в виде класса не нужно.

В следующем примере класс используется только для запуска приложения. Логика программы реализована на трех интерфейсах без использования классов!

Использование класса Sheep позволило бы сократить текст программы. Никаких других преимуществ введение этого класса не дает. Для остальных объектов определение соответствующих классов не дает ничего. Результат выполнения программы :

Анонимный класс

Можно сказать, что в приведенном выше примере использованы анонимные классы, и мы будем правы.

Но что такое анонимный класс? В спецификации Java сказано: декларация анонимного класса автоматически извлекается компилятором из выражения создания экземпляра класса. Анонимный класс является подклассом существующего класса или реализации интерфейса, и анонимный класс не имеет имени.

Обычно, для того, чтобы создать объект, необходимо сначала декларировать класс. С анонимным классом все наоборот — сначала описывается экземпляр, а потом под него подгоняется класс. Можно сказать, что анонимный класс нужен для того, чтобы узаконить существование созданного объекта. То есть, в данном случае класс — это техническое средство для упаковки реализации; небольшой, относительно автономный кусочек программы (данные + код).

Но если этот кусочек необходимо повторить, то тогда имеет смысл сделать его доступным из разных частей программы. Таким образом, класс нужен только для повторного использования. Кроме того, в большой программе выделение кода в классы улучшает ее читаемость.

Наследование интерфейса, полиморфизм

Наследование типа и полиморфизм обеспечиваются наследованием интерфейса. Пример :

Результат выполнения программы:

Таким образом, концепция интерфейсов добавляет полиморфизму второе измерение :

  • Иерархический полиморфизм в стиле C++, основанный на приведении к базовому типу классов и/или интерфейсов (см. TestShips);
  • Полиморфизм экземпляров, основанный на разных реализациях одного и того же интерфейса (см. INumber).

Наследование имеет два аспекта:

  • «быть похожим на» — наследование типа, поведения;
  • «быть устроенным как» — наследование реализации.

Наследование реализации не означает наследование типа! В практике это не встречается, потому что и в С++ и в Java невозможно наследование реализации без наследования интерфейса. В C++ интерфейс и класс неотделимы друг от друга. В Java интерфейс от класса отделить можно, но класс от интерфейса — нельзя.

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

Цукерберг рекомендует:  Фиксированная навигация второго уровня

Интерфейсы в Java

Как и класс, интерфейс может иметь методы и переменные, но методы, объявленные в интерфейсе, по умолчанию являются абстрактными (только сигнатура метода, без тела).

  • Интерфейсы определяют, что должен делать класс, а не как. Это план класса.
  • Интерфейс — это возможности, такие как Player может быть интерфейсом, и любой класс, реализующий Player, должен быть в состоянии (или должен реализовывать) move (). Таким образом, он определяет набор методов, которые должен реализовать класс.
  • Если класс реализует интерфейс и не предоставляет тела методов для всех функций, указанных в интерфейсе, тогда класс должен быть объявлен как абстрактный.
  • Пример библиотеки Java: интерфейс компаратора . Если класс реализует этот интерфейс, его можно использовать для сортировки коллекции.

Синтаксис:

Чтобы объявить интерфейс, используйте ключевое слово interface . Он используется для обеспечения полной абстракции. Это означает, что все методы в интерфейсе объявлены с пустым телом и являются открытыми, а все поля по умолчанию являются открытыми, статическими и окончательными. Класс, реализующий интерфейс, должен реализовывать все методы, объявленные в интерфейсе. Для реализации использования интерфейса реализует ключевое слово.

Почему мы используем интерфейс?

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

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

В чем смысл интерфейса? [Дубликат]

Этот вопрос казался мне немного тривиальным, пока я сам не подумал об этом.

В чем смысл интерфейса Java? Действительно ли это ответ Java на множественное наследование?

Несмотря на использование интерфейсов какое-то время, я никогда не думал об этом. Я знаю, что такое интерфейс, но все еще размышляю над тем, почему.

8 ответов

Java разрешает множественное наследование интерфейса; единственное наследование для реализации.

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


Вы видите примеры этого по всему JDK. Посмотрите на пакет java.sql — он пронизан интерфейсами. Зачем? Таким образом, разные реляционные базы данных могут свободно использовать эти методы для своего конкретного продукта. Клиенты должны иметь дело только с типами ссылок интерфейса. Изменение реляционных баз данных так же просто, как обмен одним JAR-драйвером JAR для другого. Клиенты не должны меняться. (Пока они не отклоняются от контракта.)

Динамические прокси и аспектно-ориентированное программирование зависят от интерфейсов. Они могут заменить реализацию во время выполнения таким образом.

Вы программируете против интерфейса вместо конкретной реализации, чтобы сделать ваш код гибким.

Вы программируете против IScrewDriver , чтобы вы могли использовать PhilipsScrewDriver или FlatScrewDriver , исходя из ваших потребностей.

Обычно вы используете какой-то контейнер IoC для «впрыскивания» соответствующего отвертки на свой код, но это не обязательно.

Надеемся, что примеры помогут.

В чем смысл интерфейса Java?

Дело в том, чтобы отделить API (что делать) от реализации (как это сделать).

Интерфейсы также необходимы при использовании обратных вызовов, поскольку Java не позволяет вам передавать ссылки на функции.

Действительно ли это ответ Java на множественное наследование?

Между прочим, да. Это дает вам некоторые из особенностей МИ, без неприятных осложнений.

Существует много причин, по которым у Java есть собственная конструкция для множественного наследования, т. е. —- +: = 0 =: + —- и interface . Вот три, о которых я могу думать:

Он уменьшает шаблонный код (т. е. чистые виртуальные функции)

В языках, которые не имеют конструкцию abstract , вам нужно сделать много кодировки. Рассмотрим в C ++ вам нужно создать класс с чистыми виртуальными функциями:

Эквивалент в Java будет менее подробным, главным образом потому, что методы являются виртуальными (переопределяемыми) функциями. И более очевидно, что одним из артефактов является интерфейс.

Java разрешает класс со смешанным чистым виртуальным и реализацией с ключевым словом interface MyInterface < void show(); int number(); >class ConcreteImpl implements MyInterface < public void show() < System.out.println("Concrete Impl here"); >public int number() < return 1337; >> .

Он сдерживает проблему «наследования алмазов»

Одной из проблем, связанных с множественным наследованием, является волосатая проблема наследования алмазов . Подумайте, есть ли у вас язык, допускающий эту цепочку классов на языке C ++:

Подумайте о abstract , который имеет два реализованных SuperBaseClass , реализуя метод BaseClass как виртуальные функции. В свою очередь, оба SuperBaseClass реализуются с помощью BaseClass . Посмотрите, что произойдет, если вы вызываете MyImplementation в super() ? Не всегда очевидно, что произойдет, даже в C ++ разработчики, которые разработали это, окажутся в странном отладочном сеансе.

Нет ничего особенного в том, что касается наследования бриллиантов, поэтому, чтобы избежать проблем с вызовом супер-метода, вы должны проектировать их либо с помощью чистых виртуальных функций, либо с их умным дизайном. В Java, поскольку все методы виртуальны по умолчанию, вы ограничены тем фактом, что вы можете выполнять множественное наследование только с помощью MyImplementation.draw() , так как они не содержат никакой реализации (с ключевым словом interface ). Другие классы, даже implements , могут быть только наследуемыми (с помощью abstract ).

Опытные разработчики уже использовали интерфейсы как концепцию (около 1995)

Если вы вошли в шаблоны проектирования по GoF , вы заметите, что они в значительной степени зависят от интерфейсы для разработки расширяемых систем. На самом деле одним из принципов шаблонов проектирования является:

«Программа для« интерфейса », а не« реализация ». (Gang of Four 1995: 18)

Интерфейс в смысле GoF означает, что вы должны программировать на суперкласс, а не на реализацию. Одним из примеров может быть то, что вы предпочитаете вводить вашу переменную в extends , Collection , или Iterable вместо List в Java, если единственными методами, которые вы используете для объекта, является его повторение. Пример шаблона из GoF будет шаблоном стратегии , где контекст не должен знать точная стратегия, которую она даёт исполнению, но будет выполнять установленную, независимо от того, что она была.

Это меня озадачило, пока я ничего не понял:

В принципе, интерфейс — это просто класс, который содержит только абстрактные методы. Создавая такой объект в интерфейсе, вы получаете некоторую гибкость, которой у вас нет с классами.

Найдено эта ссылка быть действительно полезным. Несколько отрывков, которые имели для меня смысл:

. Разделение интерфейса и реализация является одним из основных идеи позади Java в целом. Виртуальная машина Java (JVM) для Например, это абстрактный компьютер, который определяет способ вашей программы «интерфейсы» с базовым реальным компьютером. JVM, который работает Windows — это одна реализация этого абстрактного компьютера. JVM, что работает на Macintosh — это другое. JVM, который работает на ваших наручных часах еще один

Интерфейсы дают вам больше полиморфизма, чем однонаправленные семейства классов, потому что с интерфейсами вам не нужно делать все вписывается в одно семейство классов.

Используя интерфейсы, вы отделяете части своей системы от каждого другие и генерировать более гибкий код: легче изменить, расширенный и настраиваемый.

A implements B, C не совпадает с A extends B, C

Хотя это выглядит довольно ясно, я тоже всегда был смущен, когда люди говорят, что интерфейсы отвечают на отсутствие множественного наследования.

Как эта ссылка показывает, как можно моделировать множественное наследование при использовании интерфейсов. Но интерфейс напрямую не помогает реализовать множественное наследование.

Интерфейс, я считаю, предназначен для множественного подтипирования. В приведенном выше примере A является типом B и C. Следовательно, A можно заменить либо на членов B, либо на членов C (см. эта статья в Википедии ).

Поскольку вы можете иметь несколько реализаций одного интерфейса, это также помогает в динамическом полиморфизме. Ваш код зависит от типа (интерфейса), а не от реализации (который может быть заменен динамически на основе контекста).

Да, интерфейсы являются ответом Java на множественное наследование. Совершенно ничего, что интерфейс не может сделать, что абстрактный класс не может сделать, за исключением того факта, что несколько нескольких интерфейсов могут участвовать в родословной класса, в то время как несколько классов не могут.

Итак, мы используем интерфейс вместо абстрактного класса, чтобы дать потомкам свободу расширять любой желаемый базовый класс (и реализовывать наш интерфейс), а не заставлять их расширять определенный базовый класс.

Java — Смысл интерфейсов.


Ключевое слово interface вместо class.

Архитектор интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений. Методы интерфейса, по умолчанию, имеют модификатор доступа только public ! По этому, ключевое слово public упускается в самом интерфейсе. Но вот в эго имплементациях должно быть в сигнатуре переопределяемых методов.

Интерфейс может содержать поля, но они автоматически являются статическими (static) и неизменными (final). Все методы и переменные неявно объявляются как public.

Класс, который собирается использовать определённый интерфейс, использует ключевое слово implements. Оно указывает, что интерфейс лишь определяет форму, а вам нужно наполнить кодом. Методы, которые реализуют интерфейс, должны быть объявлены как public.

Java 8 интерфейсы

Интерфейсы в Java — это некоторый контракт, описание методов, которые обязательно должны присутствовать в классе, реализующем этот интерфейс. Интерфейсы позволяют иметь несколько различных реализаций одного и того же действия, но выполняемого различными способами или с различными видами данных. Когда вы пишете какую-либо библиотеку, имеет смысл давать пользователям работать только с публичными интерфейсами. Тогда пользователи смогут заменить одну реализацию этих интерфейсов на другую без переписывания большей части кода, также вы сможете менять внутреннюю архитектуру библиотеки без необходимости переписывания зависящего клиентского кода.

Интерфейсы в Java являются ссылочными типами, как классы, но они могут содержать в себе только константы, сигнатуры методов, default методы (методы по умолчанию), static методы (статические методы) и вложенные типы. Тела методов могут быть только у статических методов и методов по умолчанию.

Нельзя создать экземпляр интерфейса. Интерфейс может быть только реализован каким-либо классом, либо наследоваться другим интерфейсом.

Пример интерфейса, описывающий общие методы для всех монстров:

Блог только про Java

Учимся программировать на Java с нуля

Обобщенные интерфейсы в Java

Помимо классов и методов, обобщенными можно объявлять и нтерфейсы. Обобщенные интерфейсы объявляются таким же образом, как и обобщенные классы.

Ниже приведен характерный тому пример. В нем создается обобщенны йинтерфейс MinMax, где объявляются методы min() и max(), которые, как предпо­лагается, должны возвращать минимальное и максимальное значения из некото­рого множества объектов.

Ниже приведен результат выполнения данной программы.

Большую часть этой программы нетрудно понять, но некоторые ключевые моменты следует все же пояснить.

Прежде всего обратите внимание на следующее объявление обобщенного интерфейса MinMax:

Как правило, обобщенный интерфейс объявляется таким же образом, как и обобщенный класс. В данном случае параметр типа T ограничивается сверху интерфейсом Comparable.

Интерфейс Comparable определен в пакете java.lang для целей сравнения объектов. Параметр его типа обозначает тип сравниваемых объектов.

Затем интерфейс MinMax реализуется в классе MyClass. Объявление класса МyClass выглядит следующим образом:

Обратите особое внимание, каким образом параметр типа T сначала объявляет­ся в классе MyClass, а затем передается интерфейсу MinMax.

Интерфейсу MinMax требуется тип класса, реализующего интерфейс Comparable, поэтому в объявле­нии класса, реализующего этот интерфейс (в данном случае — класса MyClass ), должно быть наложено такое же ограничение.

Более того, однажды наложенное ограничение уже не нужно повторять в операторе implements. В действительности это было бы даже неверно.

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

Как правило, класс, реализующий обобщенный интерфейс, должен быть так­же обобщенным — по крайней мере, в тех случаях, когда он принимает параметр типа, передаваемый далее интерфейсу.

Например, следующая попытка объявить класс MyClass приведет к ошибке:

В классе MyClass параметр типа не объявляется, поэтому передать его интер­фейсу MinMax никак нельзя.

В данном случае идентификатор параметра типа Т просто неизвестен, и поэтому компилятор выдаст ошибку.

Безусловно, если класс реализует конкретный тип обобщенного интерфейса, то реализующий класс не обязан быть обобщенным, как показано ниже.

Обобщенный интерфейс дает два преимущества. Во-первых, он может быть реализован для разных типов данных.

И во-вторых, он позволяет наложить огра­реничения на типы данных, для которых он может быть реализован. В примере ин­терфейса MinMax вместо параметра типа Т могут быть подставлены только типы классов, реализующих интерфейс Comparable.

Ниже приведена общая синтаксическая форма обобщенного интерфейса.

Здесь список_параметров_типа обозначает разделяемый запятыми список па­раметров типа.

Когда реализуется обобщенный интерфейс, следует указать аргу­рменты типа, как показано ниже.

Java — разница между extends и implements на примерах

После продолжительного программирования на C++ переходить на Java бывает болезненно. С одной стороны прославленный сборщик мусора, а с другой огромное множество принципиальных различий в подходах к программированию. Об одном таком отличии я сегодня расскажу подробнее.

Речь пойдет о наследовании в Java. В отличии от C++, где наследование могло быть множественным, здесь это не совсем так. Кроме того, привычный синтаксис через «:» заменился на целых два ключевых слова: extends и implements. Начну с первого.

Ключевое слово extends в Java

Действие ключевого слова в точности совпадает с его переводом, один класс расширяет другой, что является классическим наследованием. Правила видимости полей и методов сохранились: private доступны только в самом классе, protected в самом классе и во всех наследниках, к public методам и полям можно обращаться откуда угодно. Главное отличие от «сишного» наследования в том, что можно расширять только один класс. Я сейчас не буду рассуждать о том, насколько это удобно, скажу только, что со множественным наследованием в C++ постоянно творилась какая-то каша.


Небольшой пример наследования с помощью ключевого слова extends. Напишем класс Door, который будет описывать характеристики двери, мы можем создать объект этого класса и работать с ним, как с «просто дверью». С другой стороны напишем еще два класса: IronDoor и WoodDoor, которые будут расширять класс Door(== наследуются от класса Door), т.е. добавят свои характеристики к базовым.

Ключевое слово implements в Java

С ключевым словом implements связано чуть больше хитростей. Слово «имплементировать» можно понимать, как «реализовывать», а в тот самый момент, когда возникает слово «реализовывать», где-то недалеко появляются интерфейсы. Так вот конструкция public class Door implements Openable означает, что класс дверь реализует интерфейс «открывающийся». Следовательно класс должен переопределить все методы интерфейса. Главная фишка в том, что можно реализовывать сколь угодно много интерфейсов.

Зачем это нужно? Самый простой пример, который приходит в голову, два интерфейса: Openable и Closeble. В первом метод open, и метод close во втором. Они помогут научить нашу дверь закрываться и открываться.

В классах-потомках двери(железная и деревянная двери) тоже появятся методы открыть/закрыть, реализованные в классе Door. Но никто нам не запрещает их переопределить.

Заключение

Итак, главное отличие в том, что extends используется для наследования от класса в прямом смысле этого слова, а implements позволяет «реализовать интерфейс». На первый взгляд это кажется лишним, неудобным и непонятным, но стоит пару раз использовать по назначению и все встает на свои места. На сегодня у меня все, спасибо за внимание!

27. Java – Интерфейсы

Интерфейс — это ссылочный тип в Java. Он схож с классом. Это совокупность абстрактных методов. Класс реализует интерфейс, таким образом наследуя абстрактные методы интерфейса.

Вместе с абстрактными методами интерфейс в Java может содержать константы, обычные методы, статические методы и вложенные типы. Тела методов существуют только для обычных методов и статических методов.

Далее разберём зачем нужны интерфейсы в Java и для чего используются, разницу абстрактного класса и интерфейса.

Содержание

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

Если класс, реализующий интерфейс, не является абстрактным, все методы интерфейса должны быть определены в классе.

Чем похожи класс и интерфейс?

Интерфейс схож с классом следующим образом:

  • Интерфейс может содержать любое количество методов.
  • Интерфейс записан в файле с расширением .java, и имя интерфейса совпадает с именем файла.
  • Байт-код интерфейса находится в .class файле.
  • Интерфейсы появляются в пакетах, и их соответствующий файл байт-кода должен быть в структуре каталогов, которая совпадает с именем пакета.

Чем отличается класс от интерфейса?

Однако, интерфейс всё же отличается от класса. Отличие интерфейса от класса в Java:

  • Вы не можете создать экземпляр интерфейса.
  • В интерфейсе не содержатся конструкторы.
  • Все методы в интерфейсе абстрактные.
  • Интерфейс не может содержать поля экземпляров. Поля, которые могут появиться в интерфейсе, обязаны быть объявлены и статическими, и final.
  • Интерфейс не расширяется классом, он реализуется классом.
  • Интерфейс может расширить множество интерфейсов.

Объявление интерфейсов

Ключевое слово interface используется для объявления интерфейса. Вот пример того, как можно создать интерфейс:

Пример 1

Интерфейсы имеют следующие свойства:

  • Интерфейс абстрактный косвенно. Вам не нужно использовать ключевое слово abstract во время объявления интерфейса.
  • Каждый метод в интерфейсе косвенно абстрактным, поэтому ключевое слово abstract не нужно.
  • Методы в интерфейсе косвенно публичны.

Пример 2

Реализация интерфейса

Когда класс реализует интерфейс, вы можете представить себе, что класс словно подписывает контракт с интерфейсом, соглашаясь совершить конкретные его поведения. Если класс не исполняет все поведения интерфейса, то класс должен объявить себя абстрактным.

Класс использует ключевое слово implements для реализации интерфейса. Ключевое слово implements появляется при объявлении класса в его расширенной части.

Пример

При переопределении методов в интерфейсе, нужно следовать некоторым правилам:

  • Проверенные исключения не должны быть объявлены по методам реализации, отличным от тех, которые были объявлены методом интерфейса или подклассами тех, которые были объявлены методом интерфейса.
  • Подпись метода интерфейса и того же типа или подтипа возврата должна поддерживаться (сохраняться) при переопределении методов.
  • Сам класс реализации может быть абстрактным, а если это так, то методы интерфейса не должны быть реализованы.

При реализации интерфейсов есть некоторые правила:

  • Класс может реализовать более одного интерфейса за раз.
  • Класс может расширить только один класс, но реализовать множество интерфейсов.
  • Интерфейс может расширить другой интерфейс таким же образом, как класс расширяет другой класс.


Расширение интерфейсов

Интерфейс может расширять другой интерфейс так же, как класс другой класс. Ключевое слово extends используется для расширения интерфейса, и дочерний интерфейс наследует методы родительского интерфейса.

Приведённый интерфейс Sports расширен интерфейсами Hockey и Football.

Пример

Интерфейс Hockey имеет четыре метода, но он наследует два из Sports; таким образом, класс, который реализует Hockey, должен реализовать все шесть методов. Подобно этому, класс, который реализует Football, должен определить три метода из Football и два метода из Sports.

Расширение множества интерфейсов

Класс в Java может расширить только один родительский класс. Множественное наследование невозможно. Однако интерфейсы не классы, и интерфейс может расширить более чем один родительский интерфейс.

Ключевое слово extends используется лишь раз, а родительские интерфейсы объявляются через запятую.

Например, если интерфейс Hockey расширил и Sports, и Event, то объявление выглядело бы так:

Интерфейсы тегов

Самое распространённое использование расширения интерфейсов происходит тогда, когда родительский интерфейс не содержит каких-либо методов. Например, интерфейс MouseListener в пакете java.awt.event расширил java.util.EventListener, который определяется так:

Интерфейс без методов в нём называется интерфейсом тегов. Есть две простые дизайнерские цели для интерфейсов тегов:

Создаёт общего родителя – как в случае с интерфейсом EventListener, который расширяется множеством других в Java API, вы можете использовать интерфейс тегов, чтобы создать общего родителя среди группы интерфейсов. Например, когда интерфейс расширяет EventListener, то JVM знает, что этот конкретный интерфейс будет использоваться в сценарии делегирования событий.

Добавляет тип данных в класс – эта ситуация является источником термина «тегирование». Класс, который реализует интерфейс тегов, не должен определять какие-либо методы (т.к. интерфейс не имеет таковых), но класс становится типом интерфейса через полиморфизм.

Интерфейс (Java)

Интерфейс — важная конструкция для реализации объектно-ориентированного программирования в языке программирования Java.

В отличие от C++, Java не позволяет наследовать больше одного класса. В качестве альтернативы множественному наследованию существуют интерфейсы. Каждый класс в Java может реализовать любой набор интерфейсов. Порождать объекты от интерфейсов в Java нельзя. Доступно расширение одного интерфейса другим по аналогии с наследованием.

[править] Объявление интерфейсов

Объявление интерфейсов очень похоже на упрощённое объявление классов.

Оно начинается с заголовка. Сначала указываются модификаторы. Интерфейс может быть объявлен как public , и тогда он будет доступен для общего использования, либо модификатор доступа может не указываться, в этом случае интерфейс доступен только для типов своего пакета. Модификатор abstract для интерфейса не требуется, поскольку все интерфейсы являются абстрактными классами. Его можно указать, но делать этого не рекомендуется, чтобы не загромождать код.

Далее записывается ключевое слово interface и имя интерфейса.

После этого может следовать ключевое слово extends и список интерфейсов, от которых будет наследоваться объявляемый интерфейс. Родительских типов (классов и/или интерфейсов) может быть много — главное, чтобы не было повторений, и чтобы отношение наследования не образовывало циклической зависимости.

Наследование интерфейсов действительно очень гибкое. Так, если есть два интерфейса, A и B , причем B наследуется от A , то новый интерфейс C может наследоваться от них обоих. Впрочем, понятно, что при наследовании от B , указание наследования от A является избыточным, так как все элементы этого интерфейса и так будут получены по наследству через интерфейс B.

Затем в фигурных скобках записывается тело интерфейса.

Пример объявления интерфейса (Ошибка если Colorable и Resizable классы: The type Colorable and Resizable cannot be a superinterface of Drawable; a superinterface must be an interface): public interface Drawable extends Colorable, Resizable

Тело интерфейса состоит из объявления элементов, то есть полей-констант и абстрактных методов. Все поля интерфейса автоматически являются public final static , так что эти модификаторы указывать необязательно и даже нежелательно, чтобы не загромождать код. Поскольку поля являются финальными, необходимо их сразу инициализировать.

public interface Directions <

Все методы интерфейса являются public abstract , и эти модификаторы также необязательны.

public interface Moveable <

Как видно, описание интерфейса гораздо проще, чем объявление класса.

[править] Реализация интерфейса

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

public class ImplementingInterface implements I <

public static void main(String[] args) <

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

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

public class Correct implements A, C // класс правильно наследует методы с одинаковой сигнатурой <

class Wrong implements A, B // класс вызывает ошибку при компиляции <

Цукерберг рекомендует:  Ищи, копи и будь полезным
Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих