JavaRush/Java блог/Random/Что такое интерфейсы
fog
18 уровень

Что такое интерфейсы

Статья из группы Random
участников
Интерфейс - это публичный общепринятый контракт (соглашение), описывающий некоторое поведение. Для чего они нужны? Например, пусть у нас есть ссылка указывающая на список строк. Предположим этот в этот список в начале добавляется множество элементов, а затем, весь список обрабатывается один раз. Допустим мы решили последовать известной рекомендации и использовать класс ArrayList:
ArrayList<String> list = new ArrayList<>();
Мы написали поведение программы в 100500 строк кода, где использовался этот список строк, и для оптимизации работы использовали методы специфичные для класса ArrayList. Например ensureCapacity(). Пока строки добавляются в конец списка, у нас всё прекрасно и быстро работает. Но вот у нас возникла потребность переориентировать нашу программу на немного другой вид работ, где строки добавляются преимущественно в начало списка. Для такого характера нагрузки гораздо больше подходит LinkedList. Но если мы захотим перевести свою программу из 100500 строк кода на рельсы LinkedList, то нам понадобится отыскать и убрать использование специфичных для ArrayList методов, возможно местами сильно меняя логику отдельных участков программы. Если бы мы использовали только те методы, которые имеются и в ArrayList, и в LinkedList, то нам бы этого делать не пришлось. Мы бы могли всего лишь изменить одну строчку кода - объявление списка:
LinkedList<String> list = new LinkedList<>();
Мы можем заметить, что было бы удобней вынести объявление методов общих, для этих классов, в класс-предок, возможно абстрактный, например AbstractList. В этом случае мы могли бы объявить наш список так:
AbstractList<String> list = new ArrayList<>();
И смогли бы быстро переключить реализацию вот так:
AbstractList<String> list = new LinkedList<>();
Но в этом случае, классы, которые мы можем использовать в нашей программе, ограничены только потомками класса AbstractList, даже если если есть более подходящие под задачу классы, не являющиеся потомками AbstractList, но имеющие те же методы с тем же поведением. Как быть? Именно поэтому были изобретены интерфейсы. Интерфейс - это такое соглашение о наборе методов и их поведении, которое могут обязаться соблюдать совершенно не связанные классы, позволяя ссылаться на любой из них с помощью единой ссылки. Например так:
List<String> list;
list = new ArrayList<>();
list = new LinkedList<>();
list = new AnotherListClass<>();
Даже если AnotherListClass не имеет с классами ArrayList и LinkedList общих классов-предков, кроме Object. Хорошим примером интерфейса, является рулевое управление автомобиля - у автомобиля есть руль, педали и коробка передач. У абсолютного большинства автомобилей, эти элементы следуют одному соглашению о поведении. Например, если повернуть рулевое колесо против часовой стрелки, автомобиль совершит поворот влево, а не увеличит скорость, независимо от своей марки. Если вы умеете использовать эти элементы управления, вы без особых усилий сможете справиться с любым автомобилем, независимо от его модели, года выпуска, марки и типа двигателя. Более того, можно представить ситуацию, в которой совершенно другой вид транспорта (к примеру, космический корабль) имеет тот же интерфейс управления, что и автомобили. Если вы, умея водить автомобиль, попадёте в кресло пилота такого корабля, то сможете не потеряться и в этой ситуации. Ещё раз повторим:
  • Интерфейс это контракт (соглашение) о поведении.
  • Множество классов, даже не связанных отношением наследования, могут объявить, что они обязуются соблюдать этот контракт (имплементация интерфейса).
  • Вынесение описания поведения в отдельный интерфейс очень удобно, так как увеличивает гибкость кода, позволяя переключать реализацию (класс имплементирующий интерфейс) интерфейса, на классы не связанные отношением наследования.
Комментарии (9)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
ШкольниК
Уровень 19
16 августа 2021, 08:56
аОаОа!_=_!
fFamous
Уровень 51
16 августа 2021, 00:21
Спасибо за развернутый ответ, теперь пришло еще больше понимания. Получается, что при создании нового объекта какого-то типа, следует использовать интерфейс или класс предок в качестве типа переменной, если есть. А есть обратные случаи, когда этого делать не стоит? Спасибо!
fog
Уровень 18
16 августа 2021, 07:06
А есть обратные случаи, когда этого делать не стоит? Имхо, единственный обратный случай это как раз тогда, когда позарез требуются какие-то конкретные методы, которых нет в интерфейсе, но есть в реализующем интерфейс классе. Но тогда возникает существенный минус - код оказывается "прибит железными гвоздями" к этому классу.
fFamous
Уровень 51
16 августа 2021, 07:56
Хм, но в таком случае, наверное можно сделать нормально, когда переменная интерфейс ссылается на объект реализующий этот интерфейс, если нам вдруг надо использовать какой-то метод класса, можно сделать ((Класс объекта)переменная интерфейса).метод(); В этом случае, если я правильно думаю, мы и операцию с объектом можем провести и изменить поле, например, а ссылка как была на интерфейс, так и останется. Например, есть класс-предок Student и подкласс StudentImpl. В подклассе реализован сеттер для имени, а в предке нет. То есть, чтобы поменять имя, нам нужно привести переменную к классу объекта и использовать метод, но сама переменная не поменяется и так и останется переменной класса предка Student ;)
Student student = new StudentImpl("Josh");
((StudentImpl) student).setName("Boris");
System.out.println(student.getName());
Justinian Judge в Mega City One Master
16 августа 2021, 07:59
. Получается, что при создании нового объекта какого-то типа,
следует использовать интерфейс или класс предок в качестве
 типа переменной, если есть
да, чтобы включить полиморфизм в своем коде. Когда мы с кем-то знакомимся как мы это запоминаем в памяти? Я познакомился с носителем биологического материала на основе набора ХХ хромосом который имеет место постоянно дислокации в наборе кирпичей типа ЕК-230 организованном в прямоугольную форму, который умеет создавать резонанс в диапазоне 150-2100 Гц при помощи стали вытянутой в цилиндрическую форму? Или мы запомним "Я познакомился с ДЕВУШКОЙ, которая живет в ДОМЕ, она МУЗЫКАНТ, играет на скрипке" Все что выделено это абстракции, мы ими оперируем постоянно, поскольку невозможно в голове держать 1000 деталей да и смысл. Почему в джаве сделали интерфейсы, они придают архитектурную гибкость, абстрактный класс во многом чем заменял интерфейс, особенно в первых версиях джавы, но благодаря интерфейсов удалось реализовать в джаве множественное наследование типов (множественное наследование классов нету, и поэтому сложно было придать одному классу поведение нескольких классов без выстраивания цепочки наследования, что ни разу не гибко, поскольку может мы не хотели привязывать отношениями Родитель - Потомок ). Поэтому, интерфейсы это про гибкость.
Justinian Judge в Mega City One Master
16 августа 2021, 08:07
есть класс-предок Student и подкласс StudentImpl.
Классы потомки - Impl не называют обычно, я не встречал. Impl добавляется в классы РЕАЛИЗУЮЩИМИ интерфейс, то есть Student - интерфейс Студент StudentImpl - класс, который реализует интерфейс Студент Если говорить о абстрактных классах или классах родителях: StudentParent, Human это будут названия для классов родителей условно AbstractStudent - название для абстрактного класса class Student extends ..
((StudentImpl) student).setName("Boris");
так можно, но нужно держать в голове, что явное приведение нужно делать только тогда, когда без него никак, это источник потенциальных ошибок в рантайме, когда мы можем в другом участке передать какой-то не тот тип, и программа скомпилируется, но вызовет ошибку в рантайме. Но это условный пример, понимание всех моментов придет исключительно на практике, когда ты набьешь руку на задачах и увидишь осмысленный код схожий с реальностью, по состоянию на сейчас, нужно просто воспринимать как есть, задавать вопросы если есть и решать задачи. Понимание сейчас точно не придет, но если для того, чтобы хоть как-то сложить картинку из того что есть, то конечно можно и нужно прояснять, здесь ты все верно делаешь, я просто к тому, чтобы ты держал в голове, что от тебя понимание не требуется, архитектурные вопросы, ООП дизайн, это самый сложный момент имхо для новичков, поскольку эта тема и понимание опирается исключительно на практический опыт, в никаких книжках и никактх примерах это не вычитаешь. Можно хоть 1000 раз пройти Need for speed на консольке или мобиле, но понимание как водить реальное авто придет только во время вождения реального авто. Другое дело, чтобы рассматривать архитектурные вопросы потом, нужно иметь базу, вот базовые знания синтаксиса в джава раше и даются, это фундамент для того, чтобы построить на нем потом понимание.
fFamous
Уровень 51
16 августа 2021, 08:38
Хорошо, спасибо. А на счет impl, менять было лень, Student был интерфейсом изначально, но я там тестил и сеттер применить не мог при приведении и подумал, что может через экстенд и класс проверить, а на деле оказалось, что я сеттер сделал приватным xD
Justinian Judge в Mega City One Master
16 августа 2021, 09:58
А на счет impl, менять было лень, либо пиши правильно, либо не пиши вообще, impl обозначает класс, имплементирующий интерфейс. Ты же навряд бы написал такой коммент: "Когда я объявляю коллекцию вот так String cat = new BufferedReader(5.0d) ; у меня не работает что-то" И на вопрос - что ты такое, ты бы ответил "та то просто лень было переделать. С точки зрения кода - твой код валиден, просто уже в пояснениях ты говоришь что там родитель-наследник, это конфузит. Есть вещи, которые можно упрощать, но нельзя писать джава, имея ввиду пайтон или писать интерфейс, имея ввиду класс-родитель. То как ты пишешь даже такие комменты, свой код формирует твои привычки.
fFamous
Уровень 51
16 августа 2021, 10:12
Согласен 👌