Les classes abstraites peuvent contenir à la fois des méthodes abstraites et des méthodes concrètes (avec une implémentation), ainsi que des états (champs de données). Une classe ne peut hériter que d'une seule classe abstraite. Les classes abstraites sont utilisées lorsque l'on doit regrouper une certaine logique et état pour une hiérarchie d'héritiers.
Les interfaces définissent uniquement les signatures des méthodes (jusqu'à Java 8), depuis Java 8, elles peuvent contenir des méthodes avec des implémentations par défaut et des méthodes statiques, mais n'ont pas d'état. Une classe peut implémenter de nombreuses interfaces. Utilisez des interfaces pour définir un ensemble de comportements indépendants de la hiérarchie d'héritage.
Exemple :
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"); } }
Quelle est la différence entre une classe abstraite avec uniquement des méthodes abstraites et une interface avant Java 8 ? Peut-on les utiliser de manière interchangeable ?
Réponse : Non, pas toujours. Une interface ne peut pas contenir d'état (champs d'instance) et prend en charge l'implémentation multiple, alors que l'héritage d'une classe abstraite est unique. De plus, les signatures des méthodes d'une interface sont toujours publiques.
Histoire
Un projet nécessitait la réalisation de plusieurs interfaces se comportant de manière similaire, mais appartenant à différentes hiérarchies d'objets. Le développeur a choisi d'utiliser une classe abstraite, ce qui a rendu impossible l'utilisation de l'héritage multiple. Cela a conduit à un refactoring significatif et à une duplication de code.
Histoire
Dans un grand projet API REST, une interface avec des champs (constantes) a été créée, en supposant qu'il s'agirait de valeurs modifiables, comme dans une classe ordinaire. En conséquence, la tentative de modifier ces champs a conduit à un échec silencieux — les valeurs restaient inchangées, et le bug n'a pas été découvert pendant longtemps.
Histoire
Lors de la migration vers Java 8, les développeurs ont ajouté des méthodes avec des implémentations dans l'interface, mais ont oublié de prendre en compte que certaines classes héritaient également de méthodes homonymes via une classe abstraite. Cela a conduit à des conflits de méthodes et à des résultats inattendus lors de la résolution de l'héritage.