Интерфейсы в ООП (Java), по-простому?

1,00
р.
Объясните по-простому, на пальцах, зачем и для чего нужны интерфейсы (Java)? Все эти заумные книжные определения и формулировки, ясности совсем не прибавляют.


Ответ
Общее определение: Интерфейс — это совокупность методов и правил взаимодействия элементов системы. Другими словами, интерфейс определяет как элементы будут взаимодействовать между собой.
Интерфейс двери — наличие ручки Интерфейс автомобиля — наличие руля, педалей, рычага коробки передач Интерфейс дискового телефона — трубка + дисковый набиратель номера.
Когда вы используете эти "объекты", вы уверены в том, что вы сможете использовать их подобным образом. Благодаря тому, что вы знакомы с их интерфейсом.
В программировании что-то похожее. Почему графическую часть программы часто называют интерфейсом? Потому, что она определяет каким образом вы сможете использовать основной функционал, заложенный в программе.
"Интерфейс определяет каким образом мы можем использовать объект" - перенесем эту мысль в плоскость программирования.
Предположим, у вас в программе есть следующие типы:
// несколько базовых интерфейсов, они пустые, // т.к. их наполнение на данный момент не существенно interface Rul {} interface Pedal {} interface Kpp {}
// интерфейс описывает взаимодействие с автомобилем - т.е. его интерфейс // предоставляет другим объектам доступ к рулю и педалям // (по сути этот интерфейс соответствует автомобилю с коробкой автоматом) interface Car { Rul getRul() Pedal[] getPedali() }
// этот интерфейс расширяет базовый // и предоставляет доступ еще и к коробке передач interface CarWithKPP extends Car { Kpp getKpp() }
// а здесь у нас сам автомобиль // реализации методов опущены т.к. не существенно class SomeCar implements CarWithKpp {...}
а сейчас посмотрим, как можно пользоваться тем, что у нас есть:
// создаем новый объект SomeCar instance = new SomeCar()
// делаем какие-то действия над объектом testAction1(instance) testAction2(instance)
Как видите, используем мы их одинаково, но суть кроется в реализации методов:
void testAction1(CarWithKpp c) { c.getRul() // можно c.getPedali() // можно c.getKpp() // можно }
void testAction2(Car c) { c.getRul() // можно c.getPedali() // можно c.getKpp() // нельзя, ошибка компиляции. этот метод не определен в интерфейсе Car }
С одной стороны, тот факт, что SomeCar наследует интерфейс CarWithKpp (а посредством последнего еще и Car), позволяет нам использовать его для работы с методами testAction1, testAction2. Интерфейсы, которые реализованы (имплементированы) в классе SomeCar — предоставляют доступ к правильному его использованию. А еще использование интерфейса в сигнатуре метода гарантирует, что вы получите именно тот тип, который вам нужен.
Обратная сторона медали состоит в том, что интерфейс накладывает ограничения на использование класса. Примером этому является то, что в методе testAction2 получить доступ к методу getKpp уже невозможно. Таким образом, можно скрыть методы и свойства, которые объявлены в интерфейсе CarWithKpp, а также методы, объявленные в классе SomeCar (если их нет в интерфейсе). Оба метода могут использовать только тот набор "средств", которые им доступны (это определяется интерфейсом).