1. Classe abstraite : révision des bases
Avant de passer à la comparaison, rappelons ce qu’est une classe abstraite.
Une classe abstraite — c’est une classe qui ne peut pas être instanciée directement (on ne peut pas écrire new Animal()), mais qui peut contenir des méthodes ordinaires (avec implémentation) ainsi que des méthodes abstraites (sans implémentation). Une classe abstraite est souvent utilisée comme base pour d’autres classes qui héritent de son comportement et/ou sont obligées d’implémenter certaines méthodes.
Par exemple, si notre application comporte différents types de transport, on peut créer une classe abstraite Transport :
public abstract class Transport {
private String model;
public Transport(String model) {
this.model = model;
}
public String getModel() {
return model;
}
// Méthode abstraite — pas d'implémentation, uniquement la déclaration
public abstract void move();
// Méthode ordinaire — l'implémentation est présente
public void printInfo() {
System.out.println("Modèle de transport : " + model);
}
}
Particularités d’une classe abstraite :
- Peut contenir des champs (état).
- Peut contenir des méthodes implémentées.
- Peut contenir des méthodes abstraites (obligatoires à implémenter dans les sous-classes).
- Impossible de créer une instance directement.
- Utilisée pour un comportement et un état communs.
2. Interface : révision des bases
Une interface — c’est un ensemble de méthodes qu’une classe doit implémenter. Une interface ne décrit pas l’état (elle ne peut pas avoir de champs ordinaires, uniquement des constantes) et ne contient pas d’implémentations de méthodes (jusqu’à Java 8). Une interface est un contrat pur : « Si vous m’implémentez, vous devez savoir faire ceci ».
Exemple d’interface :
public interface Movable {
void move(int x, int y);
}
Particularités d’une interface :
- Ne contient pas d’état (seulement des constantes public static final).
- Jusqu’à Java 8 — uniquement des méthodes abstraites (à partir de Java 8, des méthodes default et static sont apparues, mais nous y reviendrons).
- Les méthodes sont toujours public abstract par défaut.
- Une classe peut implémenter plusieurs interfaces.
- Utilisée pour décrire des capacités, « ce que sait faire » une classe.
3. Tableau comparatif : classe abstraite vs interface
Il est temps de les comparer face à face ! Voici un tableau récapitulatif :
| Caractéristique | Classe abstraite | Interface |
|---|---|---|
| Syntaxe | |
|
| Peut-on créer une instance ? | Non | Non |
| Peut contenir des méthodes ordinaires ? | Oui | Jusqu’à Java 8 — non ; à partir de Java 8 — seulement default/static |
| Peut contenir des méthodes abstraites ? | Oui | Oui (toutes les méthodes jusqu’à Java 8 sont abstraites) |
| Peut contenir des champs (état) ? | Oui (n’importe quels champs) | Uniquement public static final (constantes) |
| Peut contenir des constructeurs ? | Oui | Non |
| Héritage | Un seul type de classe (abstraite ou ordinaire) | Peut implémenter plusieurs interfaces |
| Modificateurs des méthodes | Tous (public, protected, private) | Les méthodes sont par défaut public abstract. Depuis Java 9, on peut ajouter des méthodes private pour un usage interne à l’interface |
| Héritage via | |
|
| Usage habituel | Implémentation et état communs | Description de capacités, de rôles |
| Exemples tirés du JDK | |
|
4. Quand utiliser une interface et quand une classe abstraite ?
Utilisez une interface lorsque :
- Vous voulez décrire « ce que sait faire » une classe, sans vous soucier de la manière dont elle le fait.
- Vous avez besoin qu’une classe puisse implémenter plusieurs capacités indépendantes.
- Exemples : Comparable (peut être comparée), Serializable (peut être sérialisée), Runnable (peut être exécutée dans un thread).
Utilisez une classe abstraite lorsque :
- Vous voulez définir une implémentation et un état communs à toutes les sous-classes.
- Vous avez besoin que toutes les sous-classes possèdent certains champs ou des méthodes avec implémentation.
- L’héritage est strictement « un à un » : une classe ne peut hériter que d’une seule classe (ordinaire ou abstraite).
Analogies concrètes
- Une interface — c’est comme un « permis de conduire » : si vous l’avez, vous pouvez conduire une voiture, mais personne ne dit sur quel modèle ni comment vous vous y prenez.
- Une classe abstraite — c’est comme un « plan général d’une voiture » : toutes les voitures ont un volant, des pédales, un moteur, mais chaque marque implémente les détails à sa manière.
5. Exemples issus de la bibliothèque standard Java
Interface : Comparable
public interface Comparable<T> {
int compareTo(T o);
}
Toute classe qui implémente cette interface est tenue d’implémenter la méthode compareTo. Par exemple, String, Integer, LocalDate et bien d’autres.
Classe abstraite : AbstractList
public abstract class AbstractList<E> implements List<E> {
// Implémentation par défaut de certaines méthodes de List
// Certaines méthodes restent abstraites
}
AbstractList implémente déjà une partie du comportement des collections (par exemple, les méthodes d’ajout/suppression), mais laisse certaines méthodes abstraites afin que les sous-classes puissent les implémenter à leur manière.
6. Exemples de code : comparaison en pratique
Interface
Créons une interface et une classe qui l’implémente.
public interface Printable {
void print();
}
public class Document implements Printable {
@Override
public void print() {
System.out.println("J'imprime un document...");
}
}
Classe abstraite
Maintenant, une classe abstraite et sa sous-classe.
public abstract class Machine {
public void turnOn() {
System.out.println("La machine est allumée.");
}
public abstract void work();
}
public class Printer extends Machine {
@Override
public void work() {
System.out.println("L'imprimante imprime...");
}
}
Une classe implémente les deux : l’interface et la classe abstraite
public class SmartPrinter extends Machine implements Printable {
@Override
public void work() {
System.out.println("L'imprimante intelligente fonctionne...");
}
@Override
public void print() {
System.out.println("L'imprimante intelligente imprime...");
}
}
7. Implémentation multiple d’interfaces : pourquoi c’est génial
En Java, une classe ne peut hériter que d’une seule classe (abstraite ou ordinaire), mais elle peut implémenter autant d’interfaces que nécessaire ! Cela permet de créer des architectures flexibles et extensibles.
public interface Scannable {
void scan();
}
public class MultiFunctionPrinter extends Machine implements Printable, Scannable {
@Override
public void work() {
System.out.println("L'imprimante multifonction fonctionne...");
}
@Override
public void print() {
System.out.println("L'imprimante multifonction imprime...");
}
@Override
public void scan() {
System.out.println("L'imprimante multifonction numérise...");
}
}
Que choisir, et quand ?
- Si vous concevez une fonctionnalité de base avec un état commun (par exemple, des champs), utilisez une classe abstraite.
- Si vous voulez coller aux objets des « étiquettes de capacités » (par exemple, « sait imprimer », « sait se comparer », « sait se sérialiser ») — utilisez des interfaces.
- Si vous n’êtes pas sûr — commencez par une interface. En Java, c’est une bonne pratique : les interfaces offrent davantage de flexibilité et d’extensibilité.
8. Erreurs typiques et pièges
Erreur n° 1 : Vous essayez d’hériter de plusieurs classes — Java ne vous laissera pas faire !
Une classe ne peut hériter que d’une seule classe, mais peut implémenter de nombreuses interfaces. Par exemple, class A extends B, C — erreur, alors que class A extends B implements X, Y, Z — pas de problème.
Erreur n° 2 : Vous confondez les champs d’une interface et d’une classe.
Dans une interface, on ne peut déclarer que des constantes (public static final). Impossible de déclarer un état ordinaire, par exemple, private int count; — le compilateur vous arrêtera aussitôt.
Erreur n° 3 : Vous n’avez pas implémenté toutes les méthodes de l’interface.
Si une classe n’implémente ne serait-ce qu’une seule méthode de l’interface, elle doit être déclarée abstract ; sinon, le compilateur émettra une erreur.
Erreur n° 4 : Vous essayez d’instancier une interface ou une classe abstraite.
Ces deux types sont des « semi-produits ». On peut seulement les étendre, mais pas les instancier directement :
Printable p = new Printable(); // Erreur !
Machine m = new Machine(); // Erreur !
Erreur n° 5 : Vous pensez qu’une interface peut avoir un constructeur.
Les interfaces ne peuvent pas avoir de constructeurs, car elles ne décrivent pas l’état des objets. Seules les classes (ordinaires et abstraites) en ont.
GO TO FULL VERSION