Зачем нужны интерфейсы?
Зачем нужно наследование интерфейсов?
Зачем нужен полиморфизм?
Для тех, кто почитал и понял, как делать наследование интерфейсов, но не понял, зачем.
В прошлый раз, на примере обычной семьи Ивановых мы разобрали, зачем нужны интерфейсы.
Продолжаем оцифровывать беспокойное семейство.
У каждого человека есть какие-то привычки, которые он ни от кого не наследовал или никому не передавал по наследству - его личные привычки.
У нас задача: наделить каждого члена семьи уникальными привычками. Переводим в плоскость Java: надо реализовать в классах уникальные методы, которые будут принадлежать только этим классам.
Что же, вперед!
Это Петя:
Да ладно, это не так и сложно, главное мыслить логически. Ведь все знают зачем нужны интерфейсы. Сейчас создадим interface ПривычкиМаши, опишем там метод хлопатьРесницами() и прихлюпывать() имплементируем его к Маше и дело в шляпе. Ну и что, что методы с таким названием уже реализованы в других интерфейсах, один раз можно.
Действительно, кто знает какие планы у семейства Ивановых, если родиться Сережа, который унаследует привычки от Папы, Мамы, ПраДедушки и еще кого-то из четвертого колена, каждый раз создавать интерфейс, типа: interface ПривычкиСережи, и там объявлять методы, которые уже могут быть объявлены сотни раз в других интерфейсах?
Через пару, тройку поколений мы рискуем получить интерфейсы с кучей одинаковых методов, которые уже описаны в других интерфейсах, и если надо будет изменить наименование какой-нибудь привычки (а это вполне реально – ведь мир меняется), то как разобраться в этом спагетти, я не представляю.
Остается только сидеть и мечтать о чуде.
Вот если бы для каждой привычки был свой интерфейс.
Двайте представим:
Нарисованный человек прав, так на самом деле можно - теперь симуляция семейства Ивановых спасена!
Внимательный читатель, может задать вопрос: "А зачем плодить интерфейсы для каждого члена семьи? У нас есть набор действий - сразу имплементировать нужному классу."
Представим что во многих параллельных мирах существуют двойники Пети, и всем Петям надо имплементировать interface ПривычкиПети
class Петя implements ПривычкиПапы, ПривычкиМамы {
//это личные Петины привычки
public void ковырятьВНосу () {
System.out.println("Ковырь-ковырь");
}
//это унаследованные привычки
@Override
public void прихлюпывать() {
System.out.println("Хлюп");
}
@Override
public void поджимать () {
System.out.println("Поджать губки");
}
}
Это Папа:
class Папа implements ПривычкиПапы {
//это личные Папины привычки
public void чесатьБороду () {
System.out.println("Чешу бороду");
}
//это переданные привычки
@Override
public void прихлюпывать() {
System.out.println("Хлюп");
}
}
Это Мама:
class Мама implements ПривычкиМамы{
//это личные Мамины привычки
public void хлопатьРесницами () {
System.out.println("Хлоп-хлоп");
}
//это переданные привычки
@Override
public void поджимать() {
System.out.println("Поджать губки");
}
}
Идеально! Все работает как надо!
В первой статье, говорилось, что программа — это отражение реального мира. Самое интересное свойство реальности – это все время меняться. Семья Ивановых не стала исключением, у них появилась лапочка-дочка по имени Маша. И она унаследовала от Мамы привычку хлопать ресницами, а от Папы прихлюпывать. Надо вносить изменения в нашу программу.



public interface ПривычкаПрихлюпывать {
public void прихлюпывать();
}
public interface ПривычкаПоджимать {
public void поджимать();
}
public interface ПривычкаКовырятьВНосу {
public void ковырятьВНосу();
}
public interface ПривычкаХлопатьРесницами {
public void хлопатьРесницами();
}
public interface ПривычкаЧесатьБороду {
public void чесатьБороду();
}
А потом, можно было бы как в лего, с помощью множественного наследования из отдельных привычек набрать нужный нам интерфейс привычек отдельного члена семейства. Как-то так:
public interface ПривычкиМамы extends ПривычкаПоджимать, ПривычкаХлопатьРесницами {
}
public interface ПривычкиПапы extends ПривычкаЧесатьБороду, ПривычкаХлюпать {
}
public interface ПривычкиПети extends ПривычкаПоджимать, ПривычкаХлюпать,ПривычкаКовырятьВНосу {
}
public interface ПривычкиМаши extends ПривычкаХлюпать, ПривычкаХлопатьРесницами {
}
А потом просто имплементировать нужный интерфейс нужному классу, например, Маме:
class Мама implements ПривычкиМамы{
@Override
public void хлопатьРесницами () {
System.out.println("Хлоп-хлоп");
}
@Override
public void поджимать() {
System.out.println("Поджать губки");
}
}
Так же можно было бы поступить с Папой, Петей и Машей. И потом, при расширении семейства Ивановых, проблем бы с привычками не было, мы просто бы их тасовали через наследование на уровне интерфейсов, как ингредиенты в салате, и не плодили кучу методов с одинаковым наименованием. Эх, мечты, мечты…

interface ПривычкиПети extends ПривычкаПоджимать, ПривычкаХлюпать,ПривычкаКовырятьВНосу
class ПетяВселеннаяХ implements ПривычкиПети
class ПетяВселеннаяY implements ПривычкиПети
// и т.д.
А если бы общего интерфейса не было
class ПетяВселеннаяХ implements ПривычкаПоджимать, ПривычкаХлюпать,ПривычкаКовырятьВНосу
class ПетяВселеннаяY implements ПривычкаПоджимать, ПривычкаХлюпать,ПривычкаКовырятьВНосу
// и т.д.
Получается более объемный повторяющийся код.
Наследование интерфейсов делает приложение более гибким к изменениям, в частности — можно решить проблемы с повторяющимися методами.
Еще раз обратите внимание, что множественное наследование интерфейсов — разрешено.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ