Абстрактные классы могут содержать как абстрактные, так и конкретные методы (с реализацией), а также состояния (поля данных). Класс может наследоваться только от одного абстрактного класса. Абстрактные классы применяют, когда необходимо обобщить некоторую часть логики и состояния для иерархии наследников.
Интерфейсы определяют только сигнатуры методов (до Java 8), начиная с Java 8 могут содержать методы с реализацией по умолчанию (default) и статические методы, но не имеют состояния. Один класс может реализовывать множество интерфейсов. Используйте интерфейсы для задания набора поведения, независимого от иерархии наследования.
Пример:
abstract class Animal { String name; public Animal(String name) { this.name = name; } abstract void makeSound(); } interface Movable { void move(); } class Dog extends Animal implements Movable { public Dog(String name) { super(name); } void makeSound() { System.out.println("Woof!"); } public void move() { System.out.println("Runs"); } }
В чем разница между абстрактным классом с только абстрактными методами и интерфейсом до Java 8? Можно ли их использовать взаимозаменяемо?
Ответ: Нет, не всегда. Интерфейс не может содержать состояние (поля экземпляра), а также поддерживает множественную реализацию, тогда как наследование абстрактного класса — одиночное. Кроме того, сигнатуры методов интерфейса всегда public.
История
На проекте требовалось реализовать множество схожих по поведению, но относящихся к разным иерархиям объектов, интерфейсов. Разработчик выбрал использование абстрактного класса, из-за чего стало невозможно использовать множественное наследование. В дальнейшем это привело к значительному рефакторингу и дублированию кода.
История
В одном крупном REST API проекте был создан интерфейс с полями (константами), предполагая, что это будут изменяемые значения, как в обычном классе. В результате попытка изменять эти поля приводила к Silent Failure — значения оставались прежними, баг долго не находили.
История
В процессе миграции на Java 8 разработчики добавили методы с реализацией в интерфейс, однако забыли учесть, что часть классов также наследует одноимённые методы через абстрактный класс. Это привело к конфликтам методов и неожиданным результатам при разрешении наследования.