JavaRush/Java блог/Java Developer/Интерфейсы в Java
Автор
Aditi Nawghare
Инженер-программист в Siemens

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

Статья из группы Java Developer
участников
Привет! Сегодня поговорим о важном понятии в Java — интерфейсы. Слово тебе наверняка знакомо. Например, интерфейсы есть у большинства компьютерных программ и игр. В широком смысле интерфейс — некий «пульт», который связывает две взаимодействующие друг с другом стороны. Простой пример интерфейса из повседневной жизни — пульт от телевизора. Он связывает два объекта, человека и телевизор, и выполняет разные задачи: прибавить или убавить звук, переключить каналы, включить или выключить телевизор. Одной стороне (человеку) нужно обратиться к интерфейсу (нажать на кнопку пульта), чтобы вторая сторона выполнила действие. Например, чтобы телевизор переключил канал на следующий. При этом пользователю не обязательно знать устройство телевизора и то, как внутри него реализован процесс смены канала. Для чего в Java нужны интерфейсы - 1Все, к чему пользователь имеет доступ — это интерфейс. Главная задача — получить нужный результат. Какое это имеет отношение к программированию и Java? Прямое :) Создание интерфейса очень похоже на создание обычного класса, только вместо слова class мы указываем слово interface. Давай посмотрим на простейший Java-интерфейс, и разберемся, как он работает и для чего нужен:
public interface Swimmable  {

     public void swim();
}
Мы создали интерфейс Swimmable — «умеющий плавать». Это что-то вроде нашего пульта, у которого есть одна «кнопка»: метод swim() — «плыть». Как же нам этот «пульт» использовать? Для этого метод, т.е. кнопку нашего пульта, нужно имплементировать. Чтобы использовать интерфейс, его методы должны реализовать какие-то классы нашей программы. Давай придумаем класс, объекты которого подойдут под описание «умеющий плавать». Например, подойдет класс утки — Duck:
public class Duck implements Swimmable {

    public void swim() {
        System.out.println("Уточка, плыви!");
    }

    public static void main(String[] args) {

        Duck duck = new Duck();
        duck.swim();
    }
}
Что же мы здесь видим? Класс Duck «связывается» с интерфейсом Swimmable при помощи ключевого слова implements. Если помнишь, мы использовали похожий механизм для связи двух классов в наследовании, только там было слово «extends». «public class Duck implements Swimmable» можно для понятности перевести дословно: «публичный класс Duck реализует интерфейс Swimmable». Это значит, что класс, связанный с каким-то интерфейсом, должен реализовать все его методы. Обрати внимание: в нашем классе Duck прямо как в интерфейсе Swimmable есть метод swim(), и внутри него содержится какая-то логика. Это обязательное требование. Если бы мы просто написали «public class Duck implements Swimmable» и не создали бы метод swim() в классе Duck, компилятор выдал бы нам ошибку: Duck is not abstract and does not override abstract method swim() in Swimmable Почему так происходит? Если объяснять ошибку на примере с телевизором, получится, что мы даем человеку в руки пульт с кнопкой «переключить канал» от телевизора, который не умеет переключать каналы. Тут уж нажимай на кнопку сколько влезет, ничего не заработает. Пульт сам по себе не переключает каналы: он только дает сигнал телевизору, внутри которого реализован сложный процесс смены канала. Так и с нашей уткой: она должна уметь плавать, чтобы к ней можно было обратиться с помощью интерфейса Swimmable. Если она этого не умеет, интерфейс Swimmable не свяжет две стороны — человека и программу. Человек не сможет использовать метод swim(), чтобы заставить объект Duck внутри программы плыть. Теперь ты увидел более наглядно, для чего нужны интерфейсы. Интерфейс описывает поведение, которым должны обладать классы, реализующие этот интерфейс. «Поведение» — это совокупность методов. Если мы хотим создать несколько мессенджеров, проще всего сделать это, создав интерфейс Messenger. Что должен уметь любой мессенджер? В упрощенном виде, принимать и отправлять сообщения.
public interface Messenger{

     public void sendMessage();

     public void getMessage();
}
И теперь мы можем просто создавать наши классы-мессенджеры, имплементируя этот интерфейс. Компилятор сам «заставит» нас реализовать их внутри классов. Telegram:
public class Telegram implements Messenger {

    public void sendMessage() {

        System.out.println("Отправляем сообщение в Telegram!");
    }

     public void getMessage() {
         System.out.println("Читаем сообщение в Telegram!");
     }
}
WhatsApp:
public class WhatsApp implements Messenger {

    public void sendMessage() {

        System.out.println("Отправляем сообщение в WhatsApp!");
    }

     public void getMessage() {
         System.out.println("Читаем сообщение в WhatsApp!");
     }
}
Viber:
public class Viber implements Messenger {

    public void sendMessage() {

        System.out.println("Отправляем сообщение в Viber!");
    }

     public void getMessage() {
         System.out.println("Читаем сообщение в Viber!");
     }
}
Какие преимущества это дает? Самое главное из них — слабая связанность. Представь, что мы проектируем программу, в которой у нас будут собраны данные клиентов. В классе Client обязательно нужно поле, указывающее, каким именно мессенджером клиент пользуется. Без интерфейсов это выглядело бы странно:
public class Client {

    private WhatsApp whatsApp;
    private Telegram telegram;
    private Viber viber;
}
Мы создали три поля, но у клиента запросто может быть всего один мессенджер. Просто мы не знаем какой. И чтобы не остаться без связи с клиентом, приходится «заталкивать» в класс все возможные варианты. Получается, один или два из них всегда будут null, и они вообще не нужны для работы программы. Вместо этого лучше использовать наш интерфейс:
public class Client {

    private Messenger messenger;
}
Это и есть пример «слабой связанности»! Вместо того, чтобы указывать конкретный класс мессенджера в классе Client, мы просто упоминаем, что у клиента есть мессенджер. Какой именно — определится в ходе работы программы. Но зачем нам для этого именно интерфейсы? Зачем их вообще добавили в язык? Вопрос хороший и правильный! Того же результата можно добиться с помощью обычного наследования, так ведь? Класс Messenger — родительский, а Viber, Telegram и WhatsApp — наследники. Действительно, можно и так. Но есть одна загвоздка. Как ты уже знаешь, множественного наследования в Java нет. А вот множественная реализация интерфейсов — есть. Класс может реализовывать сколько угодно интерфейсов. Представь, что у нас есть класс Smartphone, у которого есть поле Application — установленное на смартфоне приложение.
public class Smartphone {

    private Application application;
}
Приложение и мессенджер, конечно, похожи, но все-таки это разные вещи. Мессенджер может быть и мобильным, и десктопным, в то время как Application — это именно мобильное приложение. Так вот, если бы мы использовали наследование, не смогли бы добавить объект Telegram в класс Smartphone. Ведь класс Telegram не может наследоваться одновременно от Application и от Messenger! А мы уже успели унаследовать его от Messenger, и в таком виде добавить в класс Client. Но вот реализовать оба интерфейса класс Telegram запросто может! Поэтому в классе Client мы сможем внедрить объект Telegram как Messenger, а в класс Smartphone — как Application. Вот как это делается:
public class Telegram implements Application, Messenger {

    //...методы
}

public class Client {

    private Messenger messenger;

    public Client() {
        this.messenger = new Telegram();
    }
}


public class Smartphone {

    private Application application;

    public Smartphone() {
        this.application = new Telegram();
    }
}
Теперь мы используем класс Telegram как захотим. Где-то он будет выступать в роли Application, где-то — в роли Messenger. Наверняка ты уже обратил внимание, что методы в интерфейсах всегда «пустые», то есть они не имеют реализации. Причина этого проста: интерфейс описывает поведение, а не реализует его. «Все объекты классов, имплементирующих интерфейс Swimmable, должны уметь плавать»: вот и все, что говорит нам интерфейс. Как там конкретно будет плавать рыба, утка или лошадь — вопрос к классам Fish, Duck и Horse, а не к интерфейсу. Также как переключение канала — задача телевизора. Пульт просто предоставляет тебе кнопку для этого. Впрочем, в Java8 появилось интересное дополнение — методы по умолчанию (default method). Например, в твоем интерфейсе есть 10 методов. 9 из них реализованы по-разному в разных классах, но один реализован одинаково у всех. Раньше, до выхода Java8, методы внутри интерфейсов вообще не имели реализации: компилятор сразу выдавал ошибку. Теперь же можно сделать вот так:
public interface Swimmable {

   public default void swim() {
       System.out.println("Плыви!");
   }

   public void eat();

   public void run();
}
Используя ключевое слово default, мы создали в интерфейсе метод с реализацией по умолчанию. Два других метода, eat() и run(), нам необходимо будет реализовать самим во всех классах, которые будут имплементировать Swimmable. С методом swim() этого делать не нужно: реализация будет во всех классах одинаковой. Кстати, ты уже не раз сталкивался с интерфейсами в прошлых задачах, хоть и не замечал этого сам :) Вот очевидный пример: Для чего в Java нужны интерфейсы - 2Ты работал с интерфейсами List и Set! Точнее, с их реализациями — ArrayList, LinkedList, HashSet и прочими. На этой же схеме видно пример, когда один класс реализует сразу несколько интерфейсов. Например, LinkedList реализует интерфейсы List и Deque (двусторонняя очередь). Ты знаком и с интерфейсом Map, а точнее, с его реализаций — HashMap. Кстати, на этой схеме ты можешь увидеть одну особенность: интерфейсы могут быть унаследованы друг от друга. Интерфейс SortedMap унаследован от Map, а Deque наследуется от очереди Queue. Это нужно, если ты хочешь показать связь интерфейсов между собой, но при этом один интерфейс является расширенной версией другого. Давай рассмотрим пример с интерфейсом Queue — очередь. Мы пока не проходили коллекции Queue, но они достаточно простые и устроены как обычная очередь в магазине. Добавлять элементы можно только в конец очереди, а забирать — только из начала. На определенном этапе разработчикам понадобился расширенный вариант очереди, чтобы добавлять и получать элементы можно было с обеих сторон. Так создали интерфейс Deque — двустороннюю очередь. В нем присутствуют все методы обычной очереди, ведь она является «родителем» двусторонней, но при этом добавлены новые методы.
Комментарии (217)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Максим Li Java Developer
12 ноября 2023, 21:43
Всё понятно!
Alexander Minaev
Уровень 27
27 августа 2023, 06:42
Не понял, почему вместо абстрактного класса messenger мы делаем интерфейс.. У всех плюс минус поля одинаковые, можно так же наделать абстрактных методов и реализовать в каждом классе.. Если только они уже не наследуются от чего то более важного, с другой стороны, можно тот же абстрактный класс так же наследовать, что бы в объектах было меньше дублирующегося кода.. Кажется, что пример с мессенджерами не очень удачный
DanikCH
Уровень 23
28 августа 2023, 17:11
я так понял, потому что Telegram это одновременно и aplication и messenger и мы не можем написать
public class Telegram extend Messenger, Aplication
ибо множественное наследование в java запрещено, а вот наследоваться от нескольких интерфейсов можно(точнее сказать что класс не наследует несколько интерфейсов, а реализует несколько)
Alexander Minaev
Уровень 27
28 августа 2023, 17:49
Однако, можно 1 сделать абстрактных классом, а другой имплементить как интерфейс.. Вся разница же в том, что у одного есть поля состояний, а у интерфейса только реализация. Другое дело, что если мы делаем только условный телеграмм, не касаясь ват сапа и т. д., то тогда можно говорить только про интерфейсы без абстрактного класса, потому что класс будет один.
Islam Yunusov
Уровень 30
19 сентября 2023, 13:31
Что вы говорите? Интерфейс описывает лишь поведение, а не реализует его. Прочтите внимательнее статью. У абстрактных классов отсутствует множественное наследование. Абстрактный класс описывает некий абстрактный объект (автомобиль, человека, кота и т. д.), а не только поведение. Если нам нужно поведение — необходимо использовать интерфейс. Если речь про концептуальный объект — мы должны использовать абстрактный класс. Подробнее тут
Pavel Fetisov
Уровень 23
15 февраля, 19:44
Согласен, можно было описать интерфейс Message на примере использования в ВК, Телеграмме, почте и смс. Во всех этих "классах" можно получать и принимать сообщения, но все они не являются мессенджерами. Более того, можно было реализовать отдельно абстрактный класс Месенджеров, и отдельный интерфейс месседжа, Месенджер имплементировал интерфейс Месседж, а дочерние классы уже реализовывали интерфейс по-своему. А почтовики уже имели свой абстрактный класс, но тоже могли реализовывать интерфейс Месседж, так как абстрактный класс, который являлся бы родительским для всех почтовых клиентов имплементировал бы интерфейс Месендж. И наглядно видно, что есть две ветки классов, одна мессенджеры, другая почтовики и они не пересекаются в плане родительских классов, но все имеют методы интерфейса Месендж, так как и те и другие отправляют сообщения. Как-то так.
chess.rekrut
Уровень 25
21 августа 2023, 14:57
easy
JavaRusher853
Уровень 23
17 марта, 20:38
мозги у тебя easy
Rustam
Уровень 35
Student
31 июля 2023, 19:46
Зашел почитать толковые инсайты, а тут все просят лайки... Ставь лайк, если столкнулся с тем же!
Dmitry Vidonov
Уровень 29
Expert
24 августа 2023, 17:44
Ах ты хитрец!
Alexander Rozenberg
Уровень 32
25 июля 2023, 13:47
fine
Эмиль
Уровень 15
13 июля 2023, 04:40
поменяйте кнопку swim на пульте на sound, не те ассоциации
Ислам
Уровень 33
5 июня 2023, 18:39
Nice
Oraz Janov Backend Developer
5 апреля 2023, 19:47
Если у кого-то проблемы с пониманием отличия Абстрактных классов от интерфейсов можете почитать здесь. Мне очень помогло
Serega
Уровень 16
21 мая 2023, 20:22
Спасибо, тут мне понятнее стало
IrinaVyu
Уровень 22
13 июня 2023, 14:28
отличная статья
bazilradehiv1994 Java Developer в GlobalLogic
16 марта 2023, 10:48
дуже гарно написано
theylovevalera Backend Developer в шукаю роботу
6 марта 2023, 16:25
Можно лайк будь-ласка?)
Денис Java Developer в Газпромбанк
17 марта 2023, 15:48
Будь ласков? )