Мене цього також вчили, і я віддаю перевагу інтерфейсам, де це можливо (звичайно, я все ще використовую спадщину там, де є сенс).
Я думаю, що це дійсно - це відокремити ваш код від конкретних реалізацій. Скажіть, у мене є клас під назвою ConsoleWriter, і він переходить в метод, щоб щось написати, і він записує його в консоль. Тепер скажемо, що хочу перейти до друку у вікно графічного інтерфейсу. Ну тепер я повинен або змінити метод, або написати новий, який приймає GUIWriter як параметр. Якщо я почав з визначення інтерфейсу IWriter і застосував метод в IWriter, я міг би почати з ConsoleWriter (який би реалізував інтерфейс IWriter), а потім пізніше написати новий клас під назвою GUIWriter (який також реалізує інтерфейс IWriter), а потім я просто довелося б вимкнути клас, що передається.
Інша річ (що стосується C #, не впевнений у Java) - це те, що ви можете розширити лише 1 клас, але реалізувати багато інтерфейсів. Скажімо, у мене були класи на ім'я Вчитель, Математика та Історія. Тепер MathTeacher та HistoryTeacher поширюються від вчителя, але що робити, якщо ми хочемо, щоб клас представляв когось, хто є і MathTeacher, і HistoryTeacher. При спробі успадкування від декількох класів це може стати досить безладним, коли ви можете це робити лише один за одним (є спосіб, але вони не зовсім оптимальні). З інтерфейсами ви можете мати два інтерфейси під назвою IMathTeacher та IHistoryTeacher, а потім мати один клас, який поширюється на вчителя та реалізує ці два інтерфейси.
Недоліком того, що при використанні інтерфейсів є те, що іноді я бачу, що люди дублюють код (оскільки вам потрібно створити реалізацію для кожного класу інтерфейсу реалізації), проте є чітке рішення цієї проблеми, наприклад, використання таких речей, як делегати (не впевнений що таке Java еквівалент).
Думаю, що найбільшою причиною використання інтерфейсів для успадкування є роз'єднання коду реалізації, але не вважайте, що спадкування є злом, оскільки воно все ще дуже корисно.