JavaRush/Java блог/Random/Интерфейсы для тех, кому "очень интересно, но ничего не п...
Lilly
25 уровень

Интерфейсы для тех, кому "очень интересно, но ничего не понятно"

Статья из группы Random
участников
Всем привет! Данный пост написан, скорее, для тех, кто уже видел и даже решил на 12 уровне (2 уровень Java Core) несколько задач на Интерфейсы, но всё еще задаётся вопросом: "Так зачем же они нужны, если в них все равно нужно реализовывать методы, объявленные в интерфейсе??? То есть, они же не сокращают количество кода!!!". Не сомневаюсь, что эта тема раскрывается в последующих лекциях, и раскрываются более глубокие смыслы и варианты использования интерфейсов, но если вам совсем невтерпёж, то милости прошу. Изначально я хотела написать комментарий, но поняла, что это будет ооочень длинный комментарий, поэтому мы здесь. Это моя первая статья, разумную критику приветствую. Если я где-то не права - можете смело поправлять. Итак, как мы знаем, при создании своего класса мы можем наследоваться только от одного абстрактного или не абстрактного класса. Но при этом в нашем классе можно реализовывать довольно большое количество интерфейсов. При наследовании нужно руководствоваться здравым смыслом и в потомки записывать всё же родственные сущности (не будем же мы наследовать котика от бульдозера и наоборот). С интерфейсами логика немного иная: здесь важны не сами "сущности", а их "умения" или что можно с ними делать. Например, и котик, и бульдозер могут перемещаться в пространстве. То есть, в принципе, могут реализовать интерфейс CanMove или Moveable. Или же человек и кот. Они оба могут пить, только делают это по-разному: человек пьёт чай или кофе из чашки, а кот лакает водичку или молоко из миски. Но в целом они оба пьют, так что у каждого из них можно сделать свою реализацию интерфейса CanDrink. Какая нам от этого польза? Представьте, что вы делаете игру. Есть у вас, допустим, локация: река, с обеих сторон от нее берега, а дальше лес и горы. На берегу отдыхают разные виды живых существ. Внезапно приближается наводнение. Все, кто может летать - улетают. Кто не может лететь, но может бежать - бегут. Кто-то умеет плавать, так что им в принципе всё равно на это ваше наводнение (ну или они смогут выплыть на берег), хотя, некоторые из них сначала могут попытаться убежать (если умеют). Остальные, как это ни грустно, погибнут. Попробуем это реализовать. (Не пугайтесь сразу большого количества кода =) Там бОльшая часть - комментарии) Какие классы нам могут понадобиться? Начнем с абстрактного класса разных персонажей или юнитов (извините, не сильна в игровой терминологии, если ошибаюсь - поправьте). Пусть это будет класс Unit. Будем считать, что абсолютно все юниты могут перемещаться в пространстве и издавать звуки. Абстрактный класс Unit:
// Абстрактный класс для всех юнитов
public static abstract class Unit {
    // Базовый метод движения.
    // По умолчанию (если не переопределено) будет у всех наследников.
    public void move (int x, int y) {
        System.out.println("Я ( " + getClassName() + " ) просто брожу по полю на " +
                           x + " метров вправо и " + y + " метров вперед");
    }

    // Абстрактный метод, который ДОЛЖЕН ПЕРЕОПРЕДЕЛИТЬ у себя
    // КАЖДЫЙ КЛАСС, унаследованный от Unit.
    public abstract void makeSound();

    // Вспомогательный метод получения имени класса
    // без всей лишней информации.
    public String getClassName() {
        return this.getClass().getSimpleName();
    }
}
Какие интерфейсы ("навыки") нужны нашим юнитам? Они могут бегать (CanRun), плавать (CanSwim) или летать (CanFly). Кто-то может обладать несколькими навыками сразу, а у некоторых несчастных может не быть ни одного.
// Интерфейсы. КАЖДЫЙ КЛАСС, "наследующий" какой-то интерфейс,
// ДОЛЖЕН РЕАЛИЗОВАТЬ его у себя.
interface CanRun {
    void run(String action);
}
interface CanSwim {
    void swim();
}
interface CanFly {
    void fly();
}
Дальше мы создаем классы-наследники абстрактного класса Unit. Путь это будет класс Человек (Human):
// Человек НАСЛЕДУЕТ абстрактный класс Unit,
// а также РЕАЛИЗУЕТ интерфейсы CanRun, CanSwim
public static class Human extends Unit implements CanRun, CanSwim {
    // Переопределяем метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Йу-хуу! ");
    }

    // РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
    @Override
    public void run(String action) {
        System.out.println("Я ( " + this.getClassName() + " ) " + action +
                           " отсюда на двух ногах ");
    }

    // РЕАЛИЗУЕМ метод public void swim() интерфейса CanSwim
    @Override
    public void swim() {
        System.out.println("Я ( " + this.getClassName() + " ) " +
                           "с двумя руками и двумя ногами " +
                           "хорошо плаваю");
    }
}
Класс Птица (Bird). Да, я понимаю, что просто птиц не бывает, но для упрощения пусть будет просто птица, не будем делать ее абстрактной:
// Птица НАСЛЕДУЕТ абстрактный класс Unit,
// и РЕАЛИЗУЕТ интерфейс CanFly
public static class Bird extends Unit implements CanFly {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Курлык! ");
    }

    // РЕАЛИЗУЕМ метод public void fly() интерфейса CanFly
    @Override
    public void fly() {
        System.out.println("Я ( " + this.getClassName() + " ) улетел отсюда");
    }
}
А теперь создаём еще один абстрактный класс Животные (Animal), который будет унаследован от Unit:
// Абстрактный класс Животных, НАСЛЕДУЕТ абстрактный класс Unit
public static abstract class Animal extends Unit {
    // тут могут быть какие-то данные и/или методы
}
И уже его наследники Барашек (Sheep):
// Баран НАСЛЕДУЕТ класс Animal,
// и РЕАЛИЗУЕТ интерфейс CanRun
public static class Sheep extends Animal implements CanRun {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Беееее! ");
    }

    // РЕАЛИЗУЕМ метод public void run(String action) интерфейса CanRun
    @Override
    public void run(String action) {
        System.out.println("Я ( "+ this.getClassName() + " ) " + action +
                           " отсюда на четырёх копытах");
    }
}
и Ленивец (Sloth):
// Ленивец НАСЛЕДУЕТ класс Animal
// и внутри себя ПЕРЕОПРЕДЕЛЯЕТ метод
// void move(int x, int y) абстрактного класса Unit
public static class Sloth extends Animal {
    // Переопределяем абстрактный метод public void makeSound()
    // родительского абстрактного класса Unit
    @Override
    public void makeSound() {
        System.out.print("Зевает... ");
    }

	// Переопределяем метод public void move(int x, int y)
    // родительского абстрактного класса Unit
    @Override
    public void move(int x, int y) {
        System.out.println("Я ( "+ getClassName() + " ) очень медленный, поэтому " +
                           "переместился на " + (int)(x/12) + " вправо " +
                           "и на " + (int)(y/12) + " вперед");
    }
}
Метод move(int x, int y) у абстрактного класса Unit не является абстрактным, поэтому мы не обязаны его переопределять, но для Ленивца сделали небольшое замедление. Теперь переходим к действиям.
public static void main(String[] args) {
    // создаём список юнитов
    // и добавляем туда представителей разных классов
    List<Unit> units = new ArrayList<unit>();
    units.add(new Human());
    units.add(new Bird());
    units.add(new Sheep());
    units.add(new Sloth());

    // проходим в цикле по всем юнитам и вызываем метод move(int x, int y).
    // У всех, кроме ленивца, будет вызван метод базового класса.
    // У Ленивца будет вызван его переопределенный метод
    for (Unit unit : units) {
		// в самом начале ничего не происходит, юниты просто двигаются.
        unit.move((int)(Math.random()*50), (int)(Math.random()*50));
    }

    System.out.println("\n...Наводнение приближается....");
    int distanceOfFlood = (int)(Math.random()*1000); // Число от 0 до 1000
    System.out.println("...Наводнение на " + distanceOfFlood + " от берега...\n");

    // проходим в цикле по всем юнитам
    for (Unit unit : units) {
        unit.makeSound(); // у каждого юнита свой, переопределенный, звук
        // смотрим, кто что "умеет":
		// если юнит умеет летать
        if(unit instanceof CanFly)
            ((CanFly) unit).fly(); // проблем нет, улетает
        // если юнит не умеет летать, но умеет бегать
        else if(unit instanceof CanRun) {
			// смотрим на какое расстояние разлилась вода
            if(distanceOfFlood < 400) {
				// если меньше 400 (условно метров)
                ((CanRun) unit).run("убежал"); // юнит убежал
            }
            else {
				// если больше 400, юнит безуспешно пытается убежать
                ((CanRun) unit).run("пытался убежать");
				// если юнит может не только бегать, но и плавать
                if (unit instanceof CanSwim) {
                    ((CanSwim) unit).swim(); // плывёт
                }
				// иначе умирает (он хотя бы пытался)
                else unitIsDead(unit);
            }
        }
		// если юнит не летает, не бегает, но может плавать
        else if (unit instanceof CanSwim)
            ((CanSwim) unit).swim(); // плывёт
        else
			// если юнит не умеет совсем ничего - грустненько :(
            unitIsDead(unit); // умирает

        System.out.println();
    }
}

public static void unitIsDead(Unit unit) {
    System.out.println("Извините, я ( " + unit.getClassName() + " ) умер");
}
Числовые литералы 12, 50, 400 и 1000 взяты навскидку, можно задать и другие, но логика, надеюсь, понятна. Итак, как мы можем увидеть, имея абстрактный родительский класс с общими методами, мы можем вообще не задумываться какого конкретно класса тот или иной юнит, а просто вызывать эти методы (makeSound() и move()). После первого прохода в цикле, когда у всех юнитов вызывается метод move(), на экран будет выведено следующее: Интерфейсы для тех кому "очень интересно, но ничего не понятно" - 1 Очевидно, что у всех объектов, кроме ленивца выведено стандартное сообщение из метода move() абстрактного родительского класса Unit, а у ленивца метод move() был переопределен. Однако, абстрактный класс не поможет нам узнать, что "умеет" тот или иной юнит. Тут как раз в дело вступают интерфейсы. С помощью instanceof мы узнаём, может ли этот юнит совершать те или иные действия (поддерживает ли он нужный нам интерфейс), и если да, используем знакомое уже нам приведение типов, например, с помощью ((CanFly) unit).fly() приводим наш объект типа Unit к типу интерфейса CanFly и вызываем у него метод fly(). И не имеет значения какой класс у нашего объекта, важно лишь то, что он в своём "резюме" указал способность летать. Мы ему и говорим: "Ты же умеешь, вот и лети! Нам всё равно как ты это сделаешь". То есть для нас, как для разработчиков, это значит, что классы, реализующие интерфейс CanFly, могут когда угодно и как угодно менять реализацию метода fly(), могут появляться новые классы, реализующие его или же, наоборот, разработчики могут удалить некоторые из старых классов. Но до тех пор, пока этот метод выполняет заявленные функции, и объект летит, нас это не волнует. Наш код, работающий с объектами, реализующими этот интерфейс, останется прежним, нам ничего не придётся изменять. После второго цикла, когда все пытаются спастись, в зависимости от масштаба наводнения, на экране мы увидим либо Интерфейсы для тех кому "очень интересно, но ничего не понятно" - 2 либо Интерфейсы для тех кому "очень интересно, но ничего не понятно" - 3 Без интерфейса нам бы пришлось проверять каждый объект на соответствие какому-то классу (а проверить бы пришлось все) и держать в голове навыки каждого конкретного класса. То есть вот перед нами сейчас барашек и он вроде бы умеет бегать. А если у вас таких персонажей несколько десятков или сотен разных видов (классов)? А если еще и написали их не Вы, а другой программист, так что вы даже понятия не имеете кто и что там умеет? Это было бы куда сложнее... И небольшое дополнение уже после публикации: В реальной жизни над проектом работаете не вы один. Допустим, вы делаете только логику. Все объекты, с которыми вы взаимодействуете, пишут другие программисты. Вы можете даже не знать все классы, с которыми работает ваш код. Вам нужно от них только то, чтобы они выполняли то, что вам надо. При этом все они это могут делать совершенно по-разному. Но вы, допустим, в своём коде делаете метод, который работает только с объектами классов, поддерживающих определённый интерфейс
void doSomething(CanFly f)
то есть параметром методa устанавливаете интерфейс. Не конкретный класс, а интерфейс. И все другие программисты, вызывая у себя этот ваш метод void doSomething(CanFly ) аргументами должны передать либо явно объект класса, реализующего CanFly, либо какой-то объект какого-то класса, который может быть к нему приведен:
Object obj = new Bird();
doSomething(obj); // ошибка компиляции Object cannot be converted to CanFly
doSomething((CanFly) obj); // нет ошибки, потому что obj у нас класса Bird и реализует CanFly

Bird b = new Bird();
doSomething(b); // нет ошибки

Human h = new Human() ;
doSomething(h); // ошибка компиляции
doSomething((CanFly) h); // ошибка времени выполнения ClassCastException
Вот так интерфейсы и могут быть полезны. И это далеко не все их возможности и способы применения. Дальше по курсу, наверняка, узнаем больше =) Спасибо, что дочитали до конца =)
Комментарии (30)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Denis Gritsay
Уровень 37
18 ноября 2023, 11:19
шикарная статья , просто супер. редкий случай, когда понятно ЗАЧЕМ, а не углубление в частности из за которых каша в голове
Rustam
Уровень 35
Student
31 июля 2023, 16:26
Очень понравилась статья! Хороший и оригинальный пример с лесом)
Yakov Bashkurov
Уровень 19
9 ноября 2021, 13:49
Шикарно. Спасибо за труды. Стало понятнее. ЗЫ: Ленивца очень жалко. Не знал, что в статье про язык программирования может быть столько драмы.
Lilly
Уровень 25
24 ноября 2021, 14:52
спасибо =)
🦔 Виктор веду учебный тг-канал в t.me/Javangelion Expert
26 декабря 2020, 22:36
Спасибо за труды, очень интересно, но все ещё ничего не понятно... На мой вкус перегружено примерами с избыточной реализацией не по теме, под конец можно забыть, о чём статья. Если расчёт был действительно на тех, кому очень тяжело понять, то вышло как-то нагромождённо. По интерфейсам мне очень понравились следующие материалы: Для чего в Java нужны интерфейсы. В начале этой статьи хорошо и ёмко описывается суть интерфейсов в сравнении с абстрактными классами. На пальцах и картинках доходчиво объясняют, хорошо заходит на первых парах. На Metanit'е более серьёзно и детально рассмотрены интерфейсы с ёмкими примерами в одноимённой алтимейт статье. Anyway, всё относительно и индивидуально. Ещё раз спасибо за труд! Всё получится!
hidden #2322530
Уровень 41
21 декабря 2020, 11:45
блин. а оказывается можно было уже с 10+ уровня писать статьи? ((( я ждал до 41, а пока дождался, уже передумал.
Lilly
Уровень 25
21 декабря 2020, 13:58
А мне как-то практически сразу после активации аккаунта пришло уведомление, что я могу публиковать свои статьи в группе Random. Не думала, правда, что мне эта возможность пригодится =)
hidden #2322530
Уровень 41
21 декабря 2020, 14:04
это был сарказм)
Lilly
Уровень 25
21 декабря 2020, 14:06
ах, ну тогда сорян
hidden #2322530
Уровень 41
21 декабря 2020, 14:09
да мне то что) нормальная статья, нужная)
Mejhoks
Уровень 16
19 декабря 2020, 10:28
Я еще не дошел до уровня с интерфейсами, и если там плохо описана цель его существования, а тут более подробно - то даже не знаю что и думать. Я понимаю все что тут написано - оно написано очень доступно (не знаю как там по лекциям). И понятно как это работает и взаимодействует, но так и не понятно для чего увеличивать код... Мы создаем абстрактный интерфейс с одним лишь названием метода - две строчки кода, потом везде его еще дописываем в поле после класса родителя - еще два слова, и в каждом классе Оверрайдим его... А без оверрайда он пуст, то есть нужно заполнить или с него толку нет. Я бы понял если бы там было дефолтное значение и хоть для какого-то объекта не нужно было прописывать как он использует абстрактный метод. Тогда мы имели бы экономию при создание тонн объектов. Но он ведь пуст... Для чего его делать, если всем объектам которым он нужен - нужно буквально с нуля его прописать? Или я чего то вообще не догнал... Я то понимаю что если добавить все методы летать\плавать\бегать в Юнит, то надо будет как-то ими управлять при наследование. Кстате вообще не представлю как это реализуется - но почему то уверен что, наверное можно поставить условия-тумблеры ОН\ОФФ для того что бы не затирать лишние методы Оверрайдом в каждом классе-наследнике(или не Оверрайдом, я только на 10 уровень зашел - так что многого не знаю), и у нас будет возможность дефолтное значение поставить... Я вижу удобство при создание условия использования Если-естьИнтерфейсТакойто-тоВыполнять, но и там же использовано цикл для метода MakeSound - что выглядит одинаково удобно... Или я не прав?
Justinian Judge в Mega City One Master
19 декабря 2020, 10:54
Увеличивание кода это вообще даже не должно задумываться об этом. Например, у каждого из нас есть пасспорт. Идентификационный код. Миллиарды буквально идут на то, чтобы эти паспорта выпускать, проверять, раздавать и тд. Почему бы не сделать так, чтобы у всех было одно имя. На все человечество. И сколько было бы сэкономлено! Страшно себе представить. Так почему так не делают? Потому что как организовать систему обучения, работы, доступов, как в семье вообще позвать, если все будут отзываться на одно и то же имя. так и интерфейсы, ну лишние несколько строчек. Джава это не ассемблер. В джава строчки не считают. Для того чтобы построить приложение на 2000 или 10000 классов, ты не сможешь их гибко построить и поддерживать, если не будешь использовать интерфейсы. Поэтому цель существования достаточно простая, интерфейсы в джаве были задизайнены для обеспечения гибкости разработки и частично решения проблем с множественным наследованиям, которое отключили на уровне классов, но разрешили на уровне интерфейсов. Ну и еще там ряд причин. Но при начальном изучении, нужно фокусироваться на синтаксисе. Как с BufferedReader и System.out.println, на первых уровнях не нужно фокусироваться, как именно работает BufferedReader или как System.out.println может печатать прямо в системную консоль. Это просто конструкции - пишем так, получаем такой результат. Так и интерфейсы, пишем так, у нас такое поведение. Потом, по мере решания задач, знаний по смежных темах, все начнет складываться в общую картинку.
Mejhoks
Уровень 16
19 декабря 2020, 11:10
То-то понятно что потом виднее все будет. Я имею ввиду что не понятно конкретно для чего/в чем соль/какая главная цель делать вот так Абстракция - умеющий летать* Конь+умеющий летать* {а летает он - вот так} вместо Конь* {а летает он - вот так} Единственное я вижу, для чего это все - это возможно что бы при выделение летающих животных использовать только название класса для их поиска, а не рытье в их методах - летают они или нет... Может это экономнее??? Типа просто как триггер? Или в них можно класть еще что-то по умолчанию, и это просто у автора статьи они пустые?
Павел
Уровень 11
19 декабря 2020, 11:21
Попробуйте в книге “Паттерны проектирования” (Head First Design Patterns) разобраться с примером из первой главы (про уток), там наглядно показано преимущество интерфесов (книга не сложная, рассчитана на новичков и найти ее легко)
Lilly
Уровень 25
19 декабря 2020, 11:23
Здравствуйте! Я вроде как это объяснила, но если это всё ещё не очевидно, то попробую ещё раз. В реальной жизни над проектом работаете не вы один. Допустим, вы делаете только логику. Все объекты, с которыми вы взаимодействуете, пишут другие программисты. Вы можете даже не знать все классы, с которыми работает ваш код. Вам нужно от них только то, чтобы они выполняли то, что вам надо. При этом все они это могут делать совершенно по-разному. Но вы, допустим, в своём коде делаете метод
void doSomething(CanFly f)
то есть параметром методa устанавливате интерфейс. Не конкретный класс, а интерфейс. И все другие программисты, вызывая у себя этот ваш метод void doSomething(CanFly ) аргументами должны передать либо явно объект класса, реализующего CanFly, либо какой-то объект какого-то класса, который может быть к нему приведен:
Object obj = new Bird();
doSomething(obj); // ошибка компиляции Object cannot be converted to CanFly
doSomething((CanFly) obj); // нет ошибки, потому что obj у нас класса Bird и реализует CanFly
Bird b = new Bird();
doSomething(b); // нет ошибки,
Human h = new Human() ;
doSomething(h); // ошибка компиляции
doSomething((CanFly) h); // ошибка времени выполнения ClassCastException
Забегая вперёд, скажу, что с Java 8 появилась возможность реализации методов по умолчанию, да. Но на JR используют ещё Java 7, поэтому так. То, о чем вы пишете про то, чтобы все методы вместо интерфейса добавить в абстрактный класс впоследствии как раз монструозно увеличит код. А метод makeSound() в цикле потому что он есть у всех наследников. А умение летать - не у всех. Не переживайте, на последующих уровнях всё встанет на свои места. Спасибо за комментарий.
Mejhoks
Уровень 16
19 декабря 2020, 12:00
Во-во-во.... Наш уровень различен на 3, а такое ощущение что на 30(((( Я понял что чем дальше спрашивать тем более сложный ответ для меня выглядит... Мне рано туда лезть... Я насилу про класс описание понял и то не на Джава Раше, 3 часа тут сидел и только гневный коммент настрочил - мол вообще ничего не понятно... И то понял это громко сказано, я так и не смог до конца въехать как класс может выступать типом данных на подобии String, int или массивом данных String[], int[]... А ты мне пытаешься объяснить через метод у которого аргументом выступает класс(((((( Я тебе благодарен за усилия, - пойду качаться до 13, и потом перечитаю это. Надеюсь тогда все склеиться в моей дырявой голове...((
Lilly
Уровень 25
19 декабря 2020, 12:09
Терпения и удачи!) Всё получится, главное искать ответы на свои вопросы и не опускать руки) ну и не пытаться перескакивать важные темы ;-)
Mejhoks
Уровень 16
19 декабря 2020, 12:10
Да подсветило статья мол новая*, зашел прогрел лоб, словил ступор, пошел в комментах искать простой ответ - не получилось))))
Lilly
Уровень 25
19 декабря 2020, 12:24
Вы молодец! Не останавливайтесь, и всё сложится рано или поздно
hidden #2322530
Уровень 41
21 декабря 2020, 12:00
самое главное что тебе надо запомнить сейчас. Не всегда код в одну строку, работает быстрее кода в 10 строк. Не всегда код в одну строку, читается лучше, чем код в 10 строк. это не Пайтон, тут другие парадигмы.
user
Уровень 23
19 декабря 2020, 05:59
Спасибо!
Умалат работаю в totamtosyam
18 декабря 2020, 18:32
В самый раз
Lilly
Уровень 25
18 декабря 2020, 18:17
Роман, я это понимаю, спасибо! Просто на 12 уровне интерфейсы только вводятся, и я заметила, что некоторые вообще не видят, что в них есть хоть какой-то смысл. Это как один из примеров (и как мне кажется, он отчасти и о том, о чем Вы говорите). То есть, реализацию метода можно поменять в любой момент, при этом не разрушив совместимость. Мне кажется, из моего примера можно сделать такие выводы. В любом случае, это как пример для тех, кто только начал изучать интерфейсы (как и я), а до SOLID и Spring еще очень далеко =) Спасибо за комментарий!
Роман
Уровень 24
18 декабря 2020, 18:22
Ну это нормально, выдавать информацию частями, а не "вываливать" знания 11 класника на 2 класника:) постепенно все узнают цель и способы использования интерфейсов, это неизбежно, для тех кто будет идти к своей цели:) для примера я примерно только на 5 уровне начал, не понял, а начал понимать, что вообще за конструеция такая Person person = new Person():, всему свое время как говорится:) за статью спасибо:)
Виталий Уpoвень 240, Киев
18 декабря 2020, 18:30
В смысле в них нет смысла?))) Интерфейсы - это о поведении объекта. Например: Собачки, Уточки, рыбки - могут плавать. Но каждый из них плавает по разному. Поэтому мы к этим классам крепим интерфейс "Плавает". И в каждом классе Собачки, уточки и рыбки делаем свою реализацию плавания. А если мы говорим про вещи в разработке. То есть такой интерфейс KeyListener (Считывает клавиши) Он умеет считывать нажатие клавиши, а в переопределённом методе ты говоришь что с этой клавишей делать. Есть например у тебя класс Петя, унаследованный от класса Сотрудник. И ты ему интерфейсами лепишь его поведение, Петя extends Сотрудник implements ЗавариватьЧай, ПисатьДокументацию, ПодкатыватьКЛенке. Потому что для каждого сотрудника: Петя, Вася, Коля - интерфейс ПодкатыватьКЛенке (реализация) будет разным. Петя подарит цветы, Вася сводит в кино, Коля подарит кольцо. Фсё, тут нечего мудрить
Lilly
Уровень 25
18 декабря 2020, 18:40
Виталий, ну вот Вы на 20 уровне понимаете =) А зайдите в комменты на 12м и посмотрите, сколько людей не могут понять и считают это дело бессмысленным =) Спасибо за комментарий!
Виталий Уpoвень 240, Киев
18 декабря 2020, 18:42
Но в то же время я очень хочу похвалить вас за то что уже на таком уровне уже пишите статьи. У вас хорошая усидчивость и наверное крепкие нервы, раз справляетесь со сложной подачей материала. У вас большое будущее, это точно! 🤑
Lilly
Уровень 25
18 декабря 2020, 18:43
Спасибо большое!
hidden #2322530
Уровень 41
21 декабря 2020, 11:51
так эту статью могут читать все, тут нет ограничения по возрасту уровню. некоторые и на 20х плохо их понимают. ну и тут написано общее описание же. как раз для тех уровней. без учета использования интерфейсов для аннотаций, паттернов и тд, так что кто хочет, тот почитает. не поймёт с первого раза, почитает ещё раз. когда человек покупает книгу, того же Шилдта, там тоже можно в конец книги заглянуть и ужаснуться от конструкций, но человек читает по мере понимания. а хорошая статья в закладке браузера ещё никому не помешала.
Роман
Уровень 24
18 декабря 2020, 18:11
Все таки более глобальная цель (высокоуровневая) интерфейсов, это наверное - малосвязность модулей приложения, для лучшего маштабирования, добавления новых фич, уменьшения времени разработки приложения в будущем. SOLID + Роберт Мартин "Чистая архитектура".