Интерфейсы — это больше чем интерфейсы — это поведение

Открыта

— Привет, Амиго! А вот и снова я. Хочу рассказать тебе еще об одном взгляде на интерфейсы. Понимаешь, класс – это, чаще всего модель какого-то конкретного объекта. Интерфейс же больше соответствует не объектам, а их способностям или ролям.

Интерфейсы — это больше чем интерфейсы — это поведение - 1

Например, такие вещи, как машина, велосипед, мотоцикл и колесо лучше всего представить в виде классов и объектов. А такие их способности как «могу ездить», «могу перевозить людей», «могу стоять» — лучше представить в виде интерфейсов. Смотри пример:

Код на Java Описание
interface Moveable
{
void move(String newAddress);
}
— соответствует способности передвигаться.
interface Driveable
{
void drive(Driver driver);
}
— соответствует способности управляться водителем.
interface Transport
{
void addStaff(Object staff);
Object removeStaff();
}
— соответствует способности перевозить грузы.
class Wheel implements Moveable
{
...
}
— класс «колесо». Обладает способностью передвигаться.
class Car implements Moveable, Drivable, Transport
{
...
}
— класс «машина». Обладает способностью передвигаться, управляться человеком и перевозить грузы.
class Skateboard implements Moveable, Driveable
{
...
}
— класс «скейтборд». Обладает способностью передвигаться и управляться человеком.
14
Задача
Java Core,  2 уровень,  8 лекция
Недоступна
Набираем код
Иногда думать не надо, строчить надо! Как ни парадоксально звучит, порой пальцы «запоминают» лучше, чем сознание. Вот почему во время обучения в секретном центре JavaRush вы иногда встречаете задания на набор кода. Набирая код, вы привыкаете к синтаксису и зарабатываете немного материи. А ещё — боретесь с ленью.

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

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

Представь, что ты – робот-строитель и у тебя в подчинении есть десятки роботов, каждый из которых может иметь несколько профессий. Тебе нужно срочно достроить стену. Ты просто берешь всех роботов, у которых есть способность «строитель» и говоришь им строить стену. Тебе все равно, что это за роботы. Хоть робот-поливалка. Если он умеет строить – пусть идет строить.

Вот как это выглядело бы в коде:

Код на Java Описание
static interface WallBuilder
{
void buildWall();
}
— способность «строитель стен». Понимает команду «(по)строить стену» — имеет соответствующий метод.
static class РабочийРобот implements WallBuilder
{
void buildWall()
 {}
}
static class РоботСторож implements WallBuilder
{
void buildWall()
 {}
}
static class Поливалка
{}
— роботы у которых есть эта профессия/особенность.

— для удобства я сделал классам имена на русском. Такое допускается в java, но крайне нежелательно.

— поливалка не обладает способностью строить стены (не реализует интерфейс WallBuilder).

public static void main(String[] args)
{
 //добавляем всех роботов в список
 ArrayList robots = new ArrayList();
 robots.add(new РабочийРобот());
 robots.add(new РоботСторож());
 robots.add(new Поливалка());

 //строить стену, если есть такая способность
 for (Object robot: robots)
 {
  if (robot instanceof WallBuilder)
  {
   WallBuilder builder = (WallBuilder) robot;
   builder.buildWall();
   }
  }
 }
}
— как дать им команду – построить стену?

— Чертовски интересно. Даже и не думал, что интерфейсы – такая интересная тема.

— А то! В совокупности с полиморфизмом – это вообще бомба.

Комментарии (88)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий вы должны авторизоваться
WantToSleep19 уровень, Москва
14 августа, 18:34
— соответствует способности управляться водителем.
Опечатка ? Проверьте
Владислав Шаповал16 уровень, Харьков
5 августа, 13:53
Я правильно понял? В интерфейсах мы закладываем методы, которые позже можем переопределять уже в разных наследниках? А в абстрактных классах, мы, в основном, закладываем общие переменные? Или я неправильно понял?
Владислав Шаповал16 уровень, Харьков
5 августа, 13:54
"можем" ---> "должны"
Хорс13 уровень, Харьков
4 августа, 12:22
Объясните, почему IDE на дает сделать интерфейс статическим (как в примере): http://prntscr.com/oo58j6
valijon li23 уровень, Москва
4 августа, 16:11
Как я понимаю, что если помечаешь как статически, то тогда не можешь использовать его в нестатические классах. Думаю, поэтому не разрешено!
Василий22 уровень, Санкт-Петербург
1 июля, 18:37
С точки зрения английского языка в интерфейсе Transport допущена ошибка: Staff - это штат сотрудников, а Stuff - это вещи, грузы. Поэтому метод addStaff надо заменить на addStuff, это более корректно.
Павел Рожкин18 уровень
1 мая, 05:38
Нужно больше стен https://coub.com/view/txgwl
S22 уровень, Минск
4 марта, 13:44
Какая-то пошла ерунда в лекциях. Интерфейс - это реализация, поэтому каждый метод кладем в отдельный интерфейс?! В результате класс будет имплементировать 10 интерфейсов? Не вижу никакого преимущества\смысла в интерфейсе, кроме как обязать программиста реализовать необходимый метод(ы). Почему просто не создавать необходимые методы где это нужно? - Ну, допустим, программисты тупой и забывчивый. Ну так если программист тупой и забывчивый - он создаст данный метод, но не напишет внутри нужную\правильную реализацию. Тогда можно еще всунуть в язык программирования какой-нибудь глупый функционал - например, чтобы перед компиляцией выскакивало сообщение - "Вася, а ты точно ничего не забыл и все правильно сделал?!" В результате нагородили абстрактных классов, которые имеют единственное наследование и абстрактные и неабстрактные методы, потом придумали интерфейсы с множественным наследованием, но только с асбтрактными методами. Потом придумали, что в можно и неабстрактные методы в интерфейсах. Может сразу сделали бы множественное наследование и не дурили бы мозги?
Илья17 уровень, Санкт-Петербург
4 марта, 16:23
Интерфейс это как раз не реализация) Я сам уже на практике убедился, что интерфейсы очень удобная штука. В чем преимущество не возьмусь описать своими словами, но могу порекомендовать прочесть у Шилдта на эту тему, очень хорошо поясняется. Г. Шилдт - Java Руководство для начинающих. Глава 8.
Serhii24 уровень
4 марта, 18:30
Интерфейс это не реализация. Это контракт взаимодействия между классами. То есть, если класс реализует какой-то интерфейс он обязуется реализовать и все методы этого инерфейса. Таким образом програмист работая на уровне интерфейса, может вообще не догадываться какая конкретно реализация интерфейса в данный момент используется. Интерфейсы как раз и раскрывают всю силу полиморфизма. Допустим мы написали класс зоопарка, который получает на вход обьект собаки и заставляет ее ходить.
class Dog {
    public String walking() {
        return "Я хожу как собака";
    }
}
class Zoo {
    public void startDog(Dog dog) {
        dog.walking();
    }
}
Теперь пришел заказчик и хочет добавить еще котов. Окей, мы помним что нам надо реализовать метод walking для кота
class Cat {
    public String walking() {
        return "Я хожу как котейка";
    }
}
class Zoo {
    public void startDog(Dog dog) {
        dog.walking();
   }
   public void startCat(Cat cat) {
        cat.walking();
   }
 }
Нам еще пришлось добавлять новый метод в наш старый класс Zoo. И вот тут как раз кроется проблема - нам ПОСТОЯННО прийдется добавлять новые методы в существующий класс, когда нужно будет добавить животного. И так же придется вносить изменения в класс, использующий Zoo, чтобы он вызывал соответствующий метод. Интерфейс решает это проблему, если у нас будет Dog implements Walkable и Cat implements Walkable, то классу Zoo вообще не надо ничего знать про то собака это или кот или птица или вообще человек. То есть обьекты могут вообще находиться в разных иерархиях классов Dog extends Animal и Worker extends Human, но если они оба реализуют Walkable, то класс Zoo может застивить их ходить всего лишь с помощью одного метода, который не придется менять: class Zoo { public void startWalking(Walkable anyWalkableThing) { anyWalkableThing.walk(); } }
Roman Afonin35 уровень, Санкт-Петербург
11 марта, 21:09
аж вслух протянул "а... вона как". Спасибо за объяснение!
Viktor 17 уровень
22 марта, 00:39
виден свет в конце скобочек))) спасибо!
АртемGeek25 уровень, Москва
16 апреля, 15:16
Спасибо за объяснение!
Valery20 уровень
9 мая, 07:56
Так тоже самое можно и через абстрактные классы. В чем разница интерфейса и абстрактного класса? Просто то что в интерфейсах множественное наследование?
Vgoose18 уровень, Москва
15 июня, 21:17
Дальше будет пояснено, одна из статей в 10 лекции: https://javarush.ru/groups/posts/1981-dlja-chego-v-java-nuzhnih-interfeysih
Eugene Burdeinyi26 уровень, Одесса
25 февраля, 14:49
BUILD THE WALL & CRIME WILL FALL!
Grimax23 уровень, Санкт-Петербург
26 января, 07:48
Интерфейс это получается тип и тогда объект получается может быть одновременно типа "тип - Класс" (плюс все типы классов родителей) и типов "тип - Интерфейс" которые имплементирует его класс (и все классы родители) у меня крышу сносит полиморфизм полный)
Илья26 уровень
11 января, 23:40
Зачем здесь явное приведение типа:
WallBuilder builder = (WallBuilder) robot;
robot же является наследником интерфейса (реализует интерфейс) WallBuilder. То есть здесь не сужение идет, а автоматическое расширение. Да и поскольку интерфейс по сути абстрактный класс, то объект его типа невозможно создать (и, соответственно, к нему привести)? Иначе метод bildWall() должен выполняться тот, который по идее описан в классе-родителе (так как тип объекта теперь приведен к нему). А этой реализации не существует и не может существовать (все методы абстрактные) И полиморфизм теряется напрочь. У нас в каждом классе есть своя реализация метода bildWall(), и это логично. Один робот строит стену быстро и хорошо, другой медленно и средненько и так далее. Но для полиморфизма как раз приведения типов не должно быть, должно же быть так:
for (Object robot: robots)
 {
  if (robot instanceof WallBuilder)
  {
   robot.buildWall();
   }
  }
Или я просто капец где-то нить потерял?
AlexEremenko35 уровень
12 января, 09:05
Дело в том, что robot является объектом типа Object, поэтому здесь нужно явное приведение - тут у нас сужение.
Руслан23 уровень, Москва
29 января, 18:24
Ничего не теряется, тут как раз и задействована мощь полиморфизма. У нас есть лист роботов, которые являют собой разные реализации WallBuilder-а. Нужный метод buildWall() вызывается исходя из того, объект какого конкретного класса вызывается из листа.
Dronya_3313 уровень, Москва
2 марта, 22:15
А где написано что лист именно роботов? Если туда помещаются роботы, это еще не значит что там роботы. У ArrayList нет дженериков(<тип хранимых объектов в коллекции>), которые бы указали, объекты какого типа хранить в коллекции robots.
ArrayList robots = new ArrayList(); // тоже самое что и
ArrayList<Object> robots = new ArrayList<Object>()
А чтобы там хранились роботы, надо было бы в дженериках указать их родительский класс(хотя опять же нужно будет обратное приведение в более конкретный класс, ведь потомок расширяет класс, как правило), или интерфейс, который они все реализуют, в нашем случае, как мне кажется.(тут я не совсем еще разобрался)
Dronya_3313 уровень, Москва
2 марта, 22:15
Так что, если быть точнее, то там, да, роботы, но ссылки на них там присваиваются объекту типа Object. И возвращаются от туда, тоже, являясь типом Object. Это так устроено по умолчанию. То какие методы можно использовать, решает как раз тип ссылки(Object), а не тип объекта(РабочийРобот). Тип объекта(РабочийРобот) решает только какая реализация методов, общих с его предком(объект Object), будет работать. Java так устроен, что любой класс явно или неявно является наследником класса Object, если на прямую не наследует другой класс. Хотя, по сути, тот другой класс наследуется от того-же Object. А так как в классе Object прописаны самые необходимые методы для работы объектов, и он (Object) не наследует ни каких интерфейсов, то у объекта РабочийРобот, который вернулся типом Object, будут работать только методы, которые хранил в себе его предок - класс Object, ведь он все еще его типа. Потому приведение нужно, чтоб вернуть объекту РабочийРобот его поведение(функционал). А как мы знаем класс РоботРабочий, когда-то наследовал интерфейс, то проверку на соответствие IS-A(является), а является ли объект РоботРабочий потомком интерфейса WallBuilder:
robot instanceof WallBuilder
он пройдет. И будет свою работу работать.
UnNameD16 уровень, Москва
5 декабря 2018, 15:55
Пропустил наверно, что делает строка
WallBuilder builder = (WallBuilder) robot;
?
Иван34 уровень, Москва
17 декабря 2018, 11:37
Приведение типа
Andrii Gorshunov41 уровень
4 января, 22:52
Даункастинг. Вниз (сужение, даункастинг) типа надо приводить руками. Вверх расширяется автоматически. int x = 10; byte y = (byte) x; Аналогично и с иерархией наследования.